seek for help on java generic method - java

I have following class structure,
abstract class AbstractA {...}
class A1 extends AbstractA {...}
class A2 extends AbstractA {...}
abstract class AbstractB<T extends AbstractA> {
public void handle(T a) { ... }
}
class B1 extends AbstractB<A1> {
public void handle(A1 a) {
super.handle(a);
...
}
}
class B2 extends AbstractB<A2> {
public void handle(A2 a) {
super.handle(a);
...
}
}
Now I want to implement a generic method that would take a list of AbstractB and related AbstractA as parameters. e.g.
Handler.<B1, A1>handle(listOfB1, A1);
Handler.<B2, A2>handle(listOfB2, A2);
and
Handler.<B1, A2>handle(listOfB1, A2);
Handler.<B2, A1>handle(listOfB2, A1);
is not allowed.
I tried
class Handler {
// public static <T extends AbstractB<K extends AbstractA>, K extends AbstractA> handle(List<T> list, K a) {
public static <T extends AbstractB<? extends AbstractA>, K extends AbstractA> handle(List<T> list, K a) {
for (T tmp : list) {
tmp.handle(a);
}
}
}
but both does not compile. Can anyone help and give me any clue? Thanks!

Change it to :
public static <T extends AbstractB<K>, K extends AbstractA> void handle(List<T> list, K a) {
for (T tmp : list) {
tmp.handle(a);
}
}
Note that your method was missing a return type (I'm assuming your intended a void return type).
However, the main issue was that the type bound of T should be extends AbstractB<K> and not extends AbstractB<? extends AbstractA>.
Consider what happens in your current definition of the static handle method.
The current signature of the static method allows this call :
List<B1> listOfB1;
Handler.<B1, A2>handle(listOfB1, A2);
But in the body of the static method, you can't pass an A2 instance to the handle method of a B1 instance, which is why your code doesn't pass compilation. Therefore the type bound of T must depend on K.

Related

Java PECS fail for list of parametrized classes

Consider I have class named A, class B extends A and class C extends A. I have a method
public static void listMethod(List<? extends A> list){
}
To this method I can pass any List of A subclasses and A class itself. But if I introduce next class:
class Holder<T>{
private final T val;
public Holder(T val){
this.val = val;
}
public T getVal() {
return val;
}
}
And also change my method to:
public static void listMethod(List<Holder<? extends A>> list){
}
I can't pass to this method any of List<Holder<A>>, List<Holder<B>> or List<Holder<C>>
Why does this happen, and how do I change my method or my classes so I will be able to pass List of Holders
I figured it out. As pecs says ? must extend type that your list will return. So next method works fine:
public static void listMethod(List<? extends Holder<? extends A>> list){
}

Java nested generics: what should be the formal parameter type?

I've come across a situation where it is not clear how to make the code compile, although it can be easily solved by making a new method in each subclass instead of one method in the superclass (but that looks ugly!) I've simplified my code, so that I start with a chain of classes (I declare them static just for convenience):
interface U0 { }
static class U1 implements U0 {
public int x = 1;
}
static class U2 extends U1 { }
Then, there is an abstract class that does something with some container:
static abstract class M<U extends U0, C extends List<? extends U>> {
C field;
public abstract boolean check(C c);
}
and an example of a derived class acting on U1 or any descendant of U1 (say, U2):
static class M1 extends M<U1, List<? extends U1>> {
#Override
public boolean check(List<? extends U1> c) {
return !c.isEmpty() && c.get(0).x > 0;
}
}
Now, let's say I wish to extend the container, first adding a generic class:
static class MyList<U extends U0> extends ArrayList<U> {
...
}
and a derived class that calls "check" method of M:
static class MyList1 extends MyList<U2> {
void test() {
M1 m1 = new M1();
m1.check(this);
}
}
All this works so far, but now I wish to replace the lines
M1 m1 = new M1();
m1.check(this);
with a single call
callCheck(new M1());
to some method declared in MyList. Thus, the class MyList now changes to
static class MyList<U extends U0> extends ArrayList<U> {
void callCheck(??? m) {
m.check(this);
}
}
What should be the type of the parameter m?
Also note that there can be other descendants of M with, say, C = MyList1 or some other extension of List, and still callCheck should work with those descendants as well (as long as the code
SomeM someM = new SomeM();
someM.check(this);
works, where SomeM extends M<...>)
First, I got rid of the first type parameter of M, which is useless. The U there is unused in the body of M, and only appears once inside the definition of another bound as ? extends U, but since U extends U0 that's just equivalent to ? extends U0. (If this is not your real code and U is used somewhere, it could probably be added back with some thought.)
This compiles:
interface U0 { }
static class U1 implements U0 {
public int x = 1;
}
static class U2 extends U1 { }
static abstract class M<C extends List<? extends U0>> {
C field;
public abstract boolean check(C c);
}
static class M1 extends M<List<? extends U1>> {
#Override
public boolean check(List<? extends U1> c) {
return !c.isEmpty() && c.get(0).x > 0;
}
}
static class MyList<U extends U0> extends ArrayList<U> {
void callCheck(M<? super MyList<U>> m) {
m.check(this);
}
}
static class MyList1 extends MyList<U2> {
}
//...
new MyList1().callCheck(new M1());

Can a subclass in Java override a set method and make the argument type more specific?

Say I have a class
abstract class A {
ArrayList<?> l;
public void setList(ArrayList<?> l) //set the list
}
Is it possible to do something like
class B extends A {
public void setList(ArrayList<? extends Foo> l) //Set the list }
I basically would like to specify an abstract class with a parameterised field, where a class inheriting from the first class can specify the type of the field more specifically so that it must extend some other type.
Can a subclass in Java override a set method and make the argument type more specific?
No. When overriding a method the signatures (name and argument types) have to be the same after type erasure. See JLS 8.4.2 for more information.
I basically would like to specify an abstract class with a parameterised field, where a class inheriting from the first class can specify the type of the field more specifically so that it must extend some other type.
abstract class A<T> {
public abstract void setList(ArrayList<? extends T> l);
}
class B extends A<Integer> {
#Override
public void setList(ArrayList<? extends Integer> l) {
//...
};
}
Here the compiler will perform type erasure and the signatures will be identical.
You would need to make A generic:
abstract class A<T> {
abstract void setList(List<? extends T> list);
}
And then make B something like:
class B extends A<Foo> {
#Override void setList(List<? extends Foo> list) { ...}
}
It will work if you generify the base class:
abstract class A<T> {
ArrayList<T> l;
public void setList(ArrayList<T> l) {//set the list
}
}
class B<T extends Foo> extends A<T> {
#Override
public void setList(ArrayList<T> l) {//Set the list
}
}

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.

Java generic type's inferring

I have class structure like this:
class A1,A2,..,An extends A;
class B1,B2,..,Bn extends B;
And class that converts Ai into Bi:
private B1 convert(A1 a1){}
...
private Bn convert(An an){}
How can I define single public method with signature like <? extends B> convert(<? extends A> a)?
Now I have only this approach:
B convert(A a){
if(A.getClass().equals(A1.class)){
return convert((A1)a);
}...
}
Can I use instanceof if perfomance is important and the method will be called frequently?
A more elegant solution will probably be to declare a method in A: [preferably abstract, if A is abstract]:
public abstract B toB();
Overriding classes (A1,A2,...) will have to override it and instantiate their own B object.
Code snap [the static modifier is used since I implemented it as an inner class, it is not needed and cannot be used if the classes are outer classes]:
public abstract static class A {
public abstract B toB();
}
public static class A1 extends A {
#Override
public B1 toB() {
return new B1();
}
}
public static class B {
}
public static class B1 extends B {
}
you could do something like:
public <AType extends A, BType extends B> BType convert(AType a) {...
But your could have converter interface like:
public interface Converter<AType extends A, BType extends B> {
AType convert(BType b);
BType convert(AType a);
}
Regarding the performance question, you could take a look here

Categories