Java - generic classes hierarchy and generic methods overloading - java

I have a (non abstract) generic base class A<L,M>, an abstract generic sub class B<K, L, M> extends A<L,M> and a (non abstract) class C extends B<Integer, Integer, Void> :
public class A<L,M> { }
public abstract class B<K, L, M> extends A<L,M> { }
public class C extends B<Integer, Integer, Void> { }
I have a utils class that has a few methods, two are relevant here:
public static <K, L, M> void doSomething(B<K, L, M> b) {...}
public static <L, M> void doSomething(A<L, M> a) {...}
To be clear, both methods have the same name.
If I call doSomething(c); (where C c) it goes as expected to the 1st method.
My issue is with the following code (at another class that uses the utils):
private void doSomethingMoreComplex(A a) { // a is actually C
Utils.doSomething(a);
}
So here a is actually C but it goes to the second method, I am not sure if I made a mistake in the code or this is actually the expected behavior.

This is the expected behavior. The runtime type of a is inconsequential, as this method resolution is done in compile time. Since doSomething(B) cannot be applied to an argument of type A, the method is resolved to doSomething(A).
You could handle this yourself by explicitly downcasting a:
private void doSomethingMoreComplex(A a) { // a is actually C
if (a instanceof B) {
Utils.doSomething((B) a);
} else {
Utils.doSomething(a);
}
}
... but, well, in a word - yuck.
The more idiomatic way to address this would be to have doSomething as a method of A and override (not overload) it in B.

or this is actually the expected behavior.
Yes, it's the expected behavior.
The compiler chooses which method is run, and it doesn't know that it is a B (because you've said it's an A). So it doesn't know if the first overload is safe to invoke, but it knows the second overload is.

Related

Restrict bounded type parameter A<B> and A<C>

I want to bind the type parameter of the child classes. Let's suppose these are the classes:
class A {}
class B extends A {}
class C extends A {}
I want to bind the above classes so if someone creating a class should only be able to pass self class name. Eg:
class B extends A<B> {} // valid
class C extends A<B> {} // invalid
class C extends A<C> {} // valid
The closest I could get to this is:
class A<T extends A<? super T>> {}
class B extends A<B> {}
class C extends A<B> {} // allows it, but I don't want to allow this.
How can this be achived?
Use case:
class A<T extends A<? super T>> {
private T t;
private A() {}
protected A(T t) {this.t = t;}
public T something() { return t; }
}
class B extends A<B> {
public B(B b) {super(b);}
public B anotherthing() { return this.something(); }
}
There's no way to do this at compile-time.
If you really want, you can enforce it at run-time (preventing problematic classes from being instantiated) by adding logic to A's non-private constructor:
protected A(T t) {
validateClass(getClass());
this.t = t;
}
private static void validateClass(final Class<?> clazz) {
final Type superclass = clazz.getGenericSuperclass();
if ((superclass instanceof ParameterizedType)
&& ((ParameterizedType)superclass).getRawType() == A.class
&& ((ParameterizedType)superclass)getActualTypeArguments()[0] == clazz
) {
// OK
} else {
throw new IllegalStateException(
clazz + " does not extend A<" + class.getName() + ">");
}
}
. . . but I don't think that's a great idea.
In general, although Java provides lots of features for compile-time protections, a Java program ultimately relies on developers to voluntarily write correct code. Many aspects of class contracts are documented, but not enforced. (For example, nothing forces the hashCode() and equals() methods to be consistent with each other; but if you're writing Java code, you'll read the documentation of those methods before overriding them, so will know what you need to do.) In your case, you're best off just telling developers that subclasses of A should pass themselves as the type argument to A, giving them an example, and trusting them to do it.

Why does the generic type of a superclass field not get erased to the concrete bound in a subtype?

package ir.openuniverse;
public class Main {
public static void main(String[] args) throws NoSuchFieldException {
System.out.println(A.class.getField("t").getType().getName());
}
}
class A extends B<D> {}
class B<T extends C> {
public T t;
}
class C {}
class D extends C {}
The output is ir.openuniverse.C. Why? I expect D!
EDIT:
This question wasn't about workarounds or alternative ways. So answers aren't about workarounds. For alternative ways see myself answer below.
This happens because of type erasure.
Java compiles your generic class B<T> into byte code suitable for use with all classes that may reference it, including any class that may be extending B<T>.
Since T is restricted to classes extending C, Java knows that any value that you could assign B.t will extend C, so it compiles your class into an equivalent
class B {
C t;
}
At this point any assignment of t would work; reading from t would yield C, though, so the compiler must do some "magic" to fix this. Specifically, the compiler inserts type casts in places where the subtype is known. It may also generate bridge methods if necessary. See the link at the top of the answer for the details.
During compilation, Java's type erasure change
class B<T extends C> {
public T t;
}
to :
class B<C> {
public C t;
}
Since getType() identifies the declared type for the field, the output is ir.openuniverse.C
Thanks for all's helps. Finally, I forced to change my A's definition to:
class A extends B<D> {
public D t;
}
It's sufficient for my purpose (although I don't like it at all!).
EDIT:
Above way is not a fundamental way. See #DanielPryden's first two comments below.
Alternative workaround:
class A extends B<D> {
#Override public D getT() { return super.getT(); }
}
class B<T extends C> {
private T t;
public T getT() { return t; }
}
And in main():
System.out.println(A.class.getMethod("getT").getReturnType().getName());
Output: ir.openuniverse.D
Another way:
NOTE: This is not a solution to the main problem (See #DanielPryden's fourth comment below). But maybe helps you (like me).
This workaround can be used when you have at least one instance of A:
public class Main {
public static void main(String[] args) {
A a = new A();
System.out.println(a.t.getClass().getName());
// Or via reflection:
// System.out.println(a.getClass().getField("t").get(a).getClass().getName());
}
}
class A extends B<D> {
{ t = new D(); }
}
class B<T extends C> {
public T t;
}
Output: ir.openuniverse.D

Sort ArrayList of objects that are instance of sub classes of abstract class

this my main structure:
Some of the interface methods are implemented in class A, all the rest are implemented in sub classes.
public interface I
{
// some methods
//
}
public abstract class A implements I
{
// some variables and methods
//
}
public class B extends A
{
// some variables and methods
//
}
public class C extends A
{
// some variables and methods
//
}
public abstract class D extends A
{
// some variables and methods
//
}
public class E extends D
{
// some variables and methods
//
}
public class Test
{
public static void main(String[] args)
{
ArrayList<I> la = new ArrayList<I>();
la.add(new B(..));
la.add(new C(..));
la.add(new C(..));
la.add(new B(..));
la.add(new E(..));
}
}
There are constructors in B, C and E classes.
I need to sort la by variable of class A, the variable is int data type.
I try to change to
public abstract class A implements I, Comparable<A>
But I get an error because of missing public int compareTo(A o) method in all nonabstract classes.
What I need to do ?
Thanks.
I solved it by adding
public interface I extends Comparable<I>
and in class A I changed the method compareTo to
public int compareTo(I obj)
{
return someVariable - obj.someVariable;
}
public abstract class A implements I, Comparable<A> {
// some variables and methods
#Override
public int compareTo(A o) {
return someVariable - o.someVariable;
}
}
You don't need no constructor in A to implement a compareTo method, and you have got some variables you may use for comparison. compareTo is just a method, so since you can have methods in A, you can also have a compareTo method. Since I don't know what varables you've got nor the sort order you require, you will have to fill out the method yourself.
On the other hand it could well be a good idea to have a constructor in A, but this is a completely independent question.
Edit: I didn’t think this through at first: you will of course want to do
Collections.sort(la);
This is not as straightforward as it may sound. Collections.sort() is a generic method declared according to the docs as:
static <T extends Comparable<? super T>> void sort(List<T> list)
This means that for the compiler to be happy about a call to the method, the declared element type of the list must be one that implements Comparable<U> for some type U that is either the element type or a supertype of it. Your element type is I, and I doesn’t implement Comparable. This is why you get the error message.
The solution? I don’t think the perfect solution exists. Assuming you don’t want to declare la an ArrayList<A>, another option is to declare that I is comparable:
public interface I extends Comparable<I>
Strictly speaking this is promising more about A than we can fulfil. We can compare two A objects (even when they are instances of subclasses of A), but there might be other implementations of I, and we may not know how to compare them to A. One way out is:
#Override
public int compareTo(I o) {
A oa = (A) o; // will throw ClassCastException if o is not an A
return someVariable - oa.someVariable;
}
Since A extends I and I extends Comparable<I>, we now have to declare o an I, not an A. If somebody gives us some foreign I implementation, we throw a ClassCastException, and the sorting would propagate that to the caller. However, we know that in your list all elements are instances of subclasses of A, so this won’t happen. With the above, both your call to S.sortBySome() and its call to Collections.sort() will work.

Bounded types: Multiple bounds

I have read this article here and tried to figure out how to work with bound types. What I try to achieve is a parametrized method that handles four different cases:
T extends B only
T extends B and I (here D)
T extends I only
everything else
So here is the code:
public class Main {
public static void main(String... args) {
B b = new B();
D d = new D();
I i = new I() {
};
handle("aaasd");
handle(b);
handle(d); <---- Problem 1
handle(i);
}
public static class B {
}
public static interface I {
}
public static class D extends B implements I {
}
public static <T> void handle(T objT) {
System.out.println("T");
}
private static <T extends B> void handle(T obj) {
System.out.println("B");
}
public static <T extends B & I> void handle(T objT) { <--- Problem 2
System.out.println("B+I");
}
private static <T extends I> void handle(T obj) {
System.out.println("I");
}
}
The compiler complains and says two things:
Ambiguous call
The method handle(Main.D) is ambiguous for the type Main
I guess the problem is caused by the same cause as Problem number 2. The & I clearly bounds the type of T to a subtype of B AND I thus removing ambiguity in my opinion.
Same erasure handle
Method handle(T) has the same erasure handle(Main.B) as another method in type Main
My guess is that this is the real cause for all the problems. Java somehow removes bounding to I during runtime? But when I call the method with type B this doesn't call the annoted method.
Can someone explain how I fix the problem/distinguish between B, B&I and I?
Java somehow removes bounding to I during runtime?
No, Java removes every type information at runtime (except for reflection purposes) which is called type erasure.
Using bounds the compiler would be able to translate your code to handle(Object), handle(B) and handle(I) but in the T extends B & I case the compiler would get conflicts.
AFAIK, there's no way to fix this without having a common bound, e.g. T extends D instead of T extends B & I where D extends B implements I or to change the method name or add another parameter.
Another way might be to add the logic in the B+I case to either the B or I method and check for the second condition inside, e.g.
private static <T extends B> void handle(T obj) {
if( obj instanceof I) {
System.out.println("B+I");
}
else {
System.out.println("B");
}
}
There's a concept known as type erasure that applies to all generics in Java. With generic methods, after compilation, the methods in the byte code appear as their erasure, so
public static <T> void handle(T objT) {
System.out.println("T");
}
private static <T extends B> void handle(T obj) {
System.out.println("B");
}
public static <T extends B & I> void handle(T objT) { <--- Problem 2
System.out.println("B+I");
}
private static <T extends I> void handle(T obj) {
System.out.println("I");
}
actually become
public static void handle(Object objT) {
System.out.println("T");
}
private static void handle(B obj) {
System.out.println("B");
}
public static void handle(B objT) {
System.out.println("B+I");
}
private static void handle(I obj) {
System.out.println("I");
}
The left-most bound of a type variable is what a parameter of that type gets replaced with. As you can see, both your 2nd and 3rd method have the same name and same parameter types, ie. the same signature. This cannot be allowed by the compiler.
However, the syntax of bounds forces you to provide the class type before any interface types so
<T extends I & B>
wouldn't work. It also wouldn't work because your 4th method would again have the same erasure.
Additionally, invoking
handle(d);
is a problem since both the 2nd and 4th method could handle it, none is more specific. This is known as overloading ambiguity.

Method with typed list and inheritance

I have some troubles with a method having a typed List parameter, inherited from another (typed) class.
Let's keep it simple :
public class B<T> {
public void test(List<Integer> i) {
}
}
The B class has a useless generic T, and test() want an Integer List.
Now if I do :
public class A extends B {
// don't compile
#Override
public void test(List<Integer> i) {
}
}
I get a "The method test(List) of type A must override or implement a supertype method" error, that should not happen.
But removing the type of the list works... although it doesn't depend on the class generic.
public class A extends B {
// compile
#Override
public void test(List i) {
And also defining the useless generic below to use the typed list
public class A extends B<String> {
// compile
#Override
public void test(List<Integer> i) {
So I'm clueless, the generic of B should have no influence on the type of the test() list. Does anyone have an idea of what's happening?
Thanks
You're extending the raw type of B, not the generic one. The raw one effectively does not have a test(List<Integer> i) method, but a test(List) method.
If you switch to raw types, all generics are replaced by raws, regardless of whether their type was filled in or not.
To do it properly, do
public class A<T> extends B<T>
This will use the generic type B<T>, which includes the method you want to override.
When you remove use a class without generics (and use it raw), all generics from class methods are forgotten.
Due this reason when you inform the generic type on the second case you get it working.
This:
class T<G> {
public void test(G g);
}
in this case:
class A extends T {
}
will look like this:
class T {
public void test(Object g);
}
This was a java puzzle presented on Google IO 2011 you can see video here

Categories