Enforce class declarations with Generics - java

I want to implement an abstract Hierarchy class. Any Hierarchy object should have a parent (could be null) and a set of children, both of the exact same type as the concrete implementation of Hierarchy. Can this be done with generics, and if not is there any mechanism to enforce this relationship?
Here's what I was thinking so far, but there are issues:
public abstract class Hierarchy<T extends Hierarchy<T>> {
private T parent;
private Set<T> children;
public T getRoot() {
if( parent == null ) {
return this;
} else {
return parent.getRoot();
}
}
}
The problem is with return this. It gives a compile error because this is of type Hierarchy, not T. I can't cast it, because I don't actually guarantee that this is of type T. The following declaration would compile just fine:
public class B extends Hierarchy<B> {...}
public class A extends Hierarchy<B> {...}
So is there any way I can disallow A extends Hierarchy<B> declarations?

So is there any way I can disallow A extends Hierarchy
declarations?
No, it is not possible.
You can do what you want with something like this:
public abstract class Hierarchy<T> {
private T parent;
private Set<T> children;
public static <E extends Hierarchy<E>> E getRoot(E x) {
while (x.parent != null) {
x = x.parent;
}
return x;
}
}

Related

Almost identical methods in superclass and subclass are clashing. Why?

public class TestClass {
private class Parent {
public Object returnSomething() {
return new Object();
}
}
private class Child extends Parent {
public String returnIt() {
return (String) super.returnSomething();
}
}
private class GrandChild extends Child {
public StringBuilder returnIt() {
return (StringBuilder) super.returnSomething();
}
}
}
My IDE complains about that the method in child clashing with the method in grandchild. The method definitions are different on the return type, so I don't see how they can be clashing.
Can someone help me to understand what Java is not allowing here?
In Java, method overriding is not possible with different return type unless it is covariant return types for overridden methods.
StringBuilder is not a sub class of String so compiler is not allowing to overide the method returnIt()
public final class StringBuilder extends AbstractStringBuilder
implements java.io.Serializable, Comparable<StringBuilder>, CharSequence
So, if you change return type of returnIt() in Child , it will work,
private class Child extends Parent {
public CharSequence returnIt() {
return (String) super.returnSomething();
}
}
Both methods have same name and input arguments, so the compiler qualifies them as same method, even though their return type differs.
Overriding method and changing only the return type is not acceptable by the compiler.
In order to return different type, you can :
use new method name, like (returnItAsStringBuilder)
add additional argument to your method
use generic types:
Example:
private class Child<T> extends Parent {
public T returnIt() {
return (T) super.returnSomething();
}
}
private class GrandChild extends Child<StringBuilder> {
}
But looking at this code, it will always fail. You will get ClassCastExcception, because in Parent class you are creating plain Object, which cannot be casted to anything else.
You should consider abstract parent class;
private abstract class Parent <T>{
public abstract T returnSomething();
}
private class GrandChild extends Parent<StringBuilder> {
#Override
public StringBuilder returnSomething() {
return new StringBuilder();
}
}

Implementing parent-child relationship with generics

I am trying to make a container(parent) object where you can specify type of objects(children) it can contain.
Similarly, you can specify type of parent of the child.
Parent and child needs to communicate in the future, so I need them both to have object references to each other.
This code is a simpler representation of the actual code in my app.
class Parent<T extends Child> {
ArrayList<T> childObjects;
public void addChildChild(T newChild) {
childObjects.add(newChild);
newChild.setParent(this);
}
public void newChildConnected(T connectedChild) {
System.out.println("Child connected");
}
}
class Child <T extends Parent> {
T parentObject;
public void setParent(T newParent) {
parentObject = newParent;
parentObject.newChildConnected(this);
}
}
My IDE says:
Unchecked call to 'newChildConnected(T)' as a member of raw type 'test.Parent'
I have been trying different ways to make it better with wildcards and stuff, but I this is best I can do.
So what is the correct way of implementing such a behavior?
My goal is to be able to specify the child type for parent and parent type for child and do it in the way that both children and parent are able to use functionality of each other without using the intanceof() operator and casting. (that's why I use generics after all)
Is is even possible in Java?
Your usage of generic types creates a circular type reference. If the type(or interface/base class) of the parent and child are the same, use a tree structure within a single class:
class MyObject<T> {
T parentObject;
ArrayList<T> childObjects = new ArrayList();
public void addChildChild(T newChild) {
childObjects.add(newChild);
newChild.setParent(this);
}
public void newChildConnected(T connectedChild) {
System.out.println("Child connected");
}
public void setParent(T newParent) {
parentObject = newParent;
parentObject.newChildConnected(this);
}
When using this class you must check for top level objects where parentObject == null and leaf objects where childObjects.size()==0.
If there is no common interface or base class, this is not possible to do safely.
As #Strom pointed out correctly, this cannot be done in a typesafe way without base classes or interfaces.
If you can extend a class/interface, a typesafe solution without any casts would look like this:
interface BaseParent<P extends BaseParent<P, C>, C extends BaseChild<P, C>> {
List<C> getChildren();
void setChildren(List<C> children);
P self();
default void addChild(C child) {
if (child.getParent() == null) {
child.setParent(self());
}
final ArrayList<C> newChildren = new ArrayList<>(getChildren());
newChildren.add(child);
setChildren(newChildren);
}
}
interface BaseChild<P extends BaseParent<P, C>, C extends BaseChild<P, C>> {
void setParent(P parent);
P getParent();
}
final class Parent implements BaseParent<Parent, Child> {
private List<Child> children = new ArrayList<>();
#Override
public List<Child> getChildren() {
return children;
}
#Override
public void setChildren(List<Child> children) {
this.children = children;
}
#Override
public Parent self() {
return this;
}
}
final class Child implements BaseChild<Parent, Child> {
private Parent parent;
public Child(Parent parent) {
this.parent = parent;
this.parent.addChild(this);
}
#Override
public void setParent(Parent parent) {
this.parent = parent;
}
#Override
public Parent getParent() {
return parent;
}
}
The solution uses "recursive" generics for type safety and a self-type reference to avoid casts. Both of these approaches have caveats and are not entirely safe because you have to rely on the implementor of the base/class interface to return the correct self type and define correct type parameters, but should be good enough for internal APIs.

Why cast is needed when returning this from self-bound generic in Java

In this case my IDE shows compilation error in return statement.
public class Base<T extends Base>{
public T get(){
return this;
}
}
When I add a typecast as in code bellow everything works fine, however I don't get why typecast is needed.
public class Base<T extends Base>{
public T get(){
return (T) this;
}
}
Doesn't Java replace all bounded generic occurrences with bounded type? Can someone explain what is going on under the hood and why typecast is needed?
Edit 1.
Thanks to Lothars and algrid answers it is now clear that this standalone case can cause ClassCastException. This is not safe so Base should probably be abstract.
The intent of this is to create a base class for Builder classes so that extended methods would return the type of the extending class. This is needed for method chaining. In the example bellow the return type of child.setParamOne(1) will be Child despite the fact that it is defined above in the inheritance hierarchy.
Is this code safe? Do you have any suggestions or alternatives for approaching this problem?
public abstract class Base<T extends Base>{
int paramOne;
public T setParamOne(int param){
this.paramOne = param;
return (T) this;
}
}
public final class Child extends Base<Child> {
int paramTwo;
public Child setParamTwo(int param){
this.paramTwo = param;
return this;
}
}
public static void main(String[] args) {
Child c = new Child()
.setParamOne(1)
.setParamTwo(1);
}
Why do you think that your this is of the type T? It's of the type Base<T>.
Try to run the following code and you'll get ClassCastException:
public class Main {
public static void main(String[] args) {
Base<Child> b = new Base<>();
// b.get() returns an instance of Base, not Child (however it's mistakenly cast to Child)
Child1 c = b.get();
}
public static class Base<T extends Base>{
public T get(){
return (T) this;
}
}
public static class Child extends Base {
}
}
The reason for this error is the same as the error being created for code like this:
public void myMethod(InputStream is) {
ByteArrayInputStream bais = is;
}
Just with generics. To get rid of the complier error you can do the cast as you did in your code:
public void myMethod(InputStream is) {
ByteArrayInputStream bais = (ByteArrayInputStream) is;
}
But this will fail during runtime if the passed inputstream is not a ByteArrayInputStream or a class derived from it. The same will happen with your code. Unless you only create instances of Base<Base> the cast will lead to an error when calling get.
In your example:
public class Base<T extends Base>{
public T get(){
return this;
}
}
the return statement is incorrect, because this is an instance of Base<T> and not T.
If your aim is to return the instance itself (by the way, I'm not sure why you would be doing this), the code should look like this:
public class Base<T extends Base>{
public Base<T> get(){
return this;
}
}
If your aim is to return the parameterized type, then you will probably not be able to do that. The parameterized type itself is not an instance within the Base class, but, again, just the parameterized type. If that is what you need, you can get the parameterized type class using reflection.
The conversion is unsafe because this (which has type Base<T>) may not be a T. We only know that T is a Base, but not the other way around.
There is no way to represent a "self type" in Java. So what you want to do is impossible. Instead, you can make an abstract method that forces implementing subclasses to provide a way to return a T:
public class Base<T> {
public abstract T get();
}
public final class Child extends Base<Child> {
public Child get() {
return this;
}
}

Java Generics: override method that differs in parameterized return type

I come from .Net and I'm pretty new to Java development so maybe that's a weird question:
I have a class hierarchy like:
Superclass implements GenericInterface<Superclass>
^
|
Subclass
where GenericInterface is pretty straight forward:
public interface GenericInterface<T> {
OtherGenericInterface<T> getOther();
}
and OtherGenericInterface finally uses the type parameter:
public interface OtherGenericInterface<T> {
List<Object> processType(T left, T right);
}
now when I try to implement the the interface in Superclass I simply return an anonymous type:
public class Superclass implements GenericInterface<Superclass> {
#Override
public OtherGenericInterface<Superclass> getOther() {
return new OtherGenericInterface<Superclass>() {
#Override
public List<Object> processType(T left, T right) {
...
}
};
}
}
That works fine so far but now I try to override the method in the Subclass:
public class Subclass extends Superclass (implements GenericInterface<Subclass>) {
#Override
public OtherGenericInterface<Subclass> getOther() {
...
}
}
And in there, I can not override the method with my more specific return type. Even if I re-implement the interface and declare the method in the Superclass as final it is not possible.
So my question is: Why isn't OtherInterface<MoreSpecificType> a more specific, or at least the same type (due to type erasure) because that would be the requirement to override the method right?.
This demonstrates a common misconception with Java generics - believing that a match of a class will also match subclasses (like types of parameters). That is not the case. Java generics are designed to ensure that the types match exactly. If you want wriggle room you must specify and define what room you want and how much.
Here's a version that allows you to do what you want by exactly specifying the signature as <T extends Superclass>. This may not be exactly what you are looking for but I hope it points you in the right direction.
public interface OtherGenericInterface<T> {
List<Object> processType(T left, T right);
}
public interface GenericInterface<T> {
OtherGenericInterface<T> getOther();
}
public class Superclass<T extends Superclass> implements GenericInterface<T> {
#Override
public OtherGenericInterface<T> getOther() {
return new OtherGenericInterface<T>() {
#Override
public List<Object> processType(Superclass left, Superclass right) {
return null;
}
};
}
}
public class Subclass extends Superclass {
#Override
public OtherGenericInterface<Subclass> getOther() {
return null;
}
}

Generic Method Type Safety

I have the concept of NodeTypes and Nodes. A NodeType is a bunch of meta-data which you can create Node instances from (a lot like the whole Class / Object relationship).
I have various NodeType implementations and various Node implementations.
In my AbstractNodeType (top level for NodeTypes) I have ab abstract createInstance() method that will, once implemented by the subclass, creates the correct Node instance:
public abstract class AbstractNodeType {
// ..
public abstract <T extends AbstractNode> T createInstance();
}
In my NodeType implementations I implement the method like this:
public class ThingType {
// ..
public Thing createInstance() {
return new Thing(/* .. */);
}
}
// FYI
public class Thing extends AbstractNode { /* .. */ }
This is all well and good, but public Thing createInstance() creates a warning about type safety. Specifically:
Type safety: The return type Thing for
createInstance() from the type
ThingType needs unchecked conversion
to conform to T from the type
AbstractNodeType
What am I doing wrong to cause such a warning?
How can I re-factor my code to fix this?
#SuppressWarnings("unchecked") is not good, I wish to fix this by coding it correctly, not ignoring the problem!
You can just replace <T extends AbstractNode> T with AbstractNode thanks to the magic of covariant returns. Java 5 added support, but it didn't receive the pub it deserved.
Two ways:
(a) Don't use generics. It's probably not necessary in this case. (Although that depends on the code you havn't shown.)
(b) Generify AbstractNodeType as follows:
public abstract class AbstractNodeType<T extends AbstractNode> {
public abstract T createInstance();
}
public class ThingType<Thing> {
public Thing createInstance() {
return new Thing(...);
}
}
Something like that should work:
interface Node{
}
interface NodeType<T extends Node>{
T createInstance();
}
class Thing implements Node{}
class ThingType implements NodeType<Thing>{
public Thing createInstance() {
return new Thing();
}
}
class UberThing extends Thing{}
class UberThingType extends ThingType{
#Override
public UberThing createInstance() {
return new UberThing();
}
}

Categories