Why Does the following code with Cyclic Generics not compile? - java

Following is my code
class A<B2 extends B, A2 extends A<B2, A2>> {
C<B2, A2> c;
void test() {
c.acceptParameterOfTypeA(this);
}
}
class B {
}
class C<B2 extends B, A2 extends A<B2, A2>> {
void acceptParameterOfTypeA(A2 a) {
}
}
The error occurs at c.acceptParameterOfTypeA(this);.
The error is
The method acceptParameterOfTypeA(A2) in the type C is not
applicable for the arguments (A)
From what I see, the method acceptParameterOfTypeA expects a parameter of type A, and this at the line giving the error is of type A.
What am I doing wrong? How to fix this problem?
If its important, I'm using Java8

I will again rename your classes, so that everything is more readable. So, let's have:
public class First<T extends Second, U extends First<T, U>> {
Third<T, U> c;
void test() {
c.acceptParameterOfTypeA(this);
}
}
class Second {
}
public class Third<X extends Second, Y extends First<X, Y>> {
void acceptParameterOfTypeA(Y a) {
}
}
From the definition of the c member (Third<T, U>), we can conclude that c will expose a method with this signature:
void acceptParameterOfTypeA(U a) { .. }
What is U? U is a sub-type of First<T, U>.
But if U can be substituted with First after type-erasure, this will mean that First extends First<T, First>, which is not true, because U stands for sub-type of First, which is parameterized with some concrete sub-types of Second and First.
In order to get to U, you can apply the so-called Get This approach.
First, since you need U, which is a sub-type of First, but can't get it from First, you can introduce an abstract method that returns it:
abstract class First<T extends Second, U extends First<T, U>> {
Third<T, U> c;
void test() {
c.acceptParameterOfTypeA(getU());
}
abstract U getU();
}
Then, implement a sample sub-class of First, called Fourth, which extends First with some concrete types for T and U, for example:
class Fourth extends First<Second, Fourth> {
Fourth getU() {
return this;
}
}
In the getU() method, just do return this; as this will return the correct substitute for U in the super-class.
More info:
What is the "getThis" trick?
Strategy Pattern with Generics

Simply put, c.acceptParameterOfTypeA() accepts A2. this has type A<B2, A2>, which is not known to extend A2. It's only known that A2 extends A<B2, A2>.

Based on kocko's answer, the original question had the same solution:
public class Main {
abstract class A<A2 extends A<A2, B2>, B2 extends B<A2, B2>> {
B2 b;
void test() {
b.testMethod(getThis()); //getThis() instead of this;
}
abstract A2 getThis();
}
class B<A2 extends A<A2, B2>, B2 extends B<A2, B2>> {
void testMethod(A2 a) {
}
}
public void execute() {
}
public static void main(String[] args) {
Main main = new Main();
main.execute();
}
}

We can simplify it by removing the B part which doesn't contribute to the problem -
class A<T extends A<T>>
{
void test(C<T> c)
{
c.acceptParameterOfTypeA(this); // ERROR
}
}
class C<T extends A<T>>
{
void acceptParameterOfTypeA(T a) {}
}
this type is A<T>; and the question is whether A<T> <: T, which is false.
What we really want here is "self type", so that this type is T. We don't have that in Java.
Usually we use T extends A<T> for "self type"; but it is flawed and inadequate in some use cases.
One remedy for that is T getThis(), as kocko mentioned.
You could simply do a brute cast (T)this, which is obviously correct by the intention of T.
My preferred approach is to simply omit the bound of T, and rename it to This to indicate the purpose of the type variable. Casting (This)this looks obviously correct. See my other post. That approach usually works; but it doesn't work here, since C would need This to have the bound A<This>. The deeper problem is A and C depends on each other, which might be redesigned.

Related

Be sure that an input parameter fulfills multiple type criteria

I have two interfaces, A and B. I'm writing a function, and I want it to only take in objects that use both interfaces. This is some of the things I've been trying. I hope my question is clear.
public void function((? implements A,B) thing) {
// do things
}
public <T extends A, T extends B> void function(T thing) {
// do things
}
public <T extends A,B> void function(T thing) {
// do things
}
None of these attempts worked though. Thanks in advance for any insight into this.
You could try creating a 3rd interface that extends from A & B and use that as your method marker...
public interface C extends A, B {
}
Then you would only need to worry about C...
public <T extends C> void function(T thing) {
// do things
}
This does mean that anyone calling the method MUST have implement C and not just A and B, but it should get you across the line...
That is...
public class MyAB implements A, B {
}
Can't be passed to function, MyAB must implement C

Java generic interface in collection

Can anybody explain me why I get an error only when creating B in the following code:
public class factory {
public <T> void createA(List<I<T>> c) {
A a = new A(c);//warning here
}
public <T> void createB(List<I<T>> c) {
B b = new B(c);//error here: The constructor B(List<I<T>>) is undefined
}
}
interface I<T> {
}
class B implements I<Integer> {
public B(List<I<?>> c) {
}
}
class A<T> implements I<T> {
public A(List<I<?>> c) {
}
}
B class is generic and A is not, but I have no idea why it matters in that case.
public B(List<I<?>> c) {
}
The ? is the unknown type. It is not a wild card for any other type. The compiler tells the truth, there is no constructor for the type List<I<T>> (T is not ?)
It doesn't really work for the other method too. You simply exchanged the compile error by a "unchecked conversion" warning. Because class A is parametized, you'd have to call it like that:
public <T> void createA(List<I<T>> c) {
A<T> a = new A<T>(c);
}
And voilà, say hello to the same error.
A is a generic class, which means that when you use A by itself, it is a raw type. When you use a raw type, you turn off all the generics for its methods and constructors. So for example, the following will compile:
A a = new A(new ArrayList<String>());
B is not a generic class, so using B by itself is not a raw type, and does not turn off generics.

Java Generic Conversion not as expected

If someone could resolve this puzzle for me. I am sure I am missing something !
interface a { }
class b implements a { }
class c extends ArrayList<b> { }
class d {
d(ArrayList<a> param) {}
}
class e {
public static void main(String[] arg) {
d newd = new d(new c());
}
}
This code has an error:
Error d(ArrayList<a>) is not applicable (actual argument c cannot be converted to ArrayList<a> by method invocation conversion)
Surely class c can be converted to ArrayList<a> as c extends ArrayList<b> and b implements a !
I have tried explicit conversion but it does not help.
It does not make sense either to change class c extends ArrayList<b> to extends ArrayList<a> as the purpose of c to to be a collection of b and the interface a is merely for display purposes. Also class d is a generic display class that relies on functionality expressed in the interface a and so makes no sense to change that either.
Advice would be handy !
This should suit your needs:
class d {
d(ArrayList<? extends a> param) {
}
}
c isn't an ArrayList<a> but an ArrayList<? extends a> since b is a subtype of a.
The problem is that an ArrayList is not the same thing as an ArrayList, even if b extends A. What would happen if D called param.add(new AImpl()), where AImpl was some other implementation of interface A?
For more details, see The Java Generics Tutorial section on inheritance.
(As an aside, it's also generally not a good idea to extend an ArrayList - most of the time, you want to be wrapping it, not extending it.)
Please respect java naming conventions:
interface A {
}
class B implements A {
}
class C extends ArrayList<B> {
}
class D {
D(ArrayList<A> param) {
}
}
class E {
public static void main(String[] arg) {
D newd = new D(new C());
}
}
So your assumption is that ArrayList<B> is castable to an ArrayList<A> as B implements A. This is not true, this is not how generics work. You simply cannot cast generic types.
Try doing something like:
final ArrayList<Object> myNewArrayList = new ArrayList<String>();
To distill the problem.
You need to use bounded type parameters to solve this. Changing your D class to:
class D {
D(ArrayList<? extends A> param) {
}
}
Would make it work as now you're saying: I want a collection of any type as long as it's an A.
It should be:
d(ArrayList<? extends a> param) {}
ArrayList is not a subclass of ArrayList because the former can have a's added to it while the latter cannot.

Java Generics: Multiple Inheritance in Bounded Type Parameters <T extends A & I>

I am about to create a factory which creates objects of a certain type T which extends a certain class A and another interface I. However, T must not be known. Here are the minimum declarations:
public class A { }
public interface I { }
This is the factory method:
public class F {
public static <T extends A & I> T newThing() { /*...*/ }
}
This compiles all fine.
When I try to use the method the following works fine:
A $a = F.newThing();
...while this does not:
I $i = F.newThing();
The compiler complains:
Bound mismatch: The generic method newThing() of type F is not applicable for the arguments (). The inferred type I&A is not a valid substitute for the bounded parameter
I can't understand why. It is clearly stated that "newThing returns something of a certain type T which does extend the class A and implement the interface I". When assigning to A everything works (since T extends A) but assigning to I does not (because of what?, clearly the thing returned is both an A and an I)
Also: When returning an object, say B of the type class B extends A implements I, I need to cast it to the return type T, although B matches the bounds:
<T extends A & I> T newThing() {
return (T) new B();
}
However, the compiler does not throw any warnings like UncheckedCast or the like.
Thus my question:
What is going wrong here?
Is there an easy away to achieve the desired behavior (i.e. assigning to a variable of static type A or I), like there is in solving the return-type-problem by casting, in the factory method?
Why does the assignment to A work, while to I does not?
--
EDIT: Here the complete code snippet which totally works using Eclipse 3.7, project set up for JDK 6:
public class F {
public static class A { }
public static interface I { }
private static class B extends A implements I { }
public static <T extends A & I> T newThing() {
return (T) new B();
}
public static void main(String... _) {
A $a = F.newThing();
// I $i = F.newThing();
}
}
EDIT: Here is a complete example with methods and invocation which does work at runtime:
public class F {
public static class A {
int methodA() {
return 7;
}
}
public static interface I {
int methodI();
}
private static class B extends A implements I {
public int methodI() {
return 12;
}
}
public static <T extends A & I> T newThing() {
return (T) new B();
}
public static void main(String... _) {
A $a = F.newThing();
// I $i = F.newThing();
System.out.println($a.methodA());
}
}
As for the second question:
Consider this case:
class B extends A implements I {}
class C extends A implements I {}
Now, the following uses type inference:
<T extends A & I> T newThing() {
return (T) new B();
}
So you could call this:
C c = F.newThing(); //T would be C here
You see that T could be anything that extends A and I you can't just return an instance of B. In the case above the cast could be written as (C)new B(). This would clearly result in an exception and thus the compiler issues a warning: Unchecked cast from B to T - unless you're supressing those warnings.
This doesn't do what you expect it to. T extends A & I indicates that the caller can specify any type that extends A and I, and you'll return it.
I think that one way to explain it is by replacing the type parameter with the actual type.
The parameterized signature of the methods is:
public static <T extends A & B> T newThing(){
return ...;
}
The <T extends A & B> is what is called a type parameter. The compiler would expect that this value is actually substituted with the actual type (called type argument) when you actually use it.
In the case of your method the actual type is decided by means of type inference. That is, <T extends A & B> should be replaced by a real existing type that extends A and implements B.
So, let's say that classes C and D both extends A and implements B, then if your signature were like this:
public static <T extends A & B> T newThing(T obj){
return obj;
}
Then, by type inference, your method would be evaluated as follows:
public static C newThing(C obj){
return obj;
}
if you invoke with newThing(new C()).
And would be as follows
public static D newThing(D obj){
return obj;
}
if you invoke with newThing(new D()).
This would compile just fine!
However, since you are not actually providing any kind of type to validate type inference in your method declaration, then the compiler could never be sure what is the actual type (type argument) of your type parameter <T extends A & B>.
You might expect that the actual type is C, but there may be thousands of different classes that satisfy that criteria. Which of those should the compiler use as the actual type of your type argument?
Let's say that C and D are two classes that extend A and implements B. Which of these two actual types should the compiler use as type argument for your method?
You could have even declared a type argument for which there is not even an existing type that you can use, like saying something that extends Serializable and Closable and Comparable and Appendable.
And perhaps there is not a class in the whole world that satisfies that.
As such, you must understand that the type parameter here is just a requirement for the compiler to validate the actual type that you use, a placeholder for the actual type; and this actual type must exist at the end and the compiler will use it to replace appearances of T. Therefore the actual type (type argument) must be inferable from the context.
Since the compiler cannot tell with certainty which is the actual type that you mean, basically because there is no way to determine that by type inference in this case, then you are forced to cast your type, to ensure the compiler that you know what you are doing.
As such, you could implement your method using type inference like this:
public static <T extends A & B> T newThing(Class<T> t) throws Exception{
return t.newInstance();
}
This way, you would be actually telling the compiler what is the actual type argument to be used.
Take into account that when the bytecodes are generated, the compiler must substitute T for a real type. There is no way to write method in Java like this
public static A & B newThing(){ return ... }
Right?
I hope I have explained myself! This is not simple to explain.
Simplest solution is create an abstract base class that extends and implements whatever class and interfaces you want and return that type. It doesn't matter that you're constraining your return type to extend this base class as you were already constraining the return type to its superclass.
eg.
class C {}
interface I {}
abstract class BaseClass extends C implements I {}
// ^-- this line should never change. All it is telling us that we have created a
// class that combines the methods of C and I, and that concrete sub classes will
// implement the abstract methods of C and I
class X extends BaseClass {}
class Y extends BaseClass {}
public class F {
public static BaseClass newThing() {
return new X();
}
public static void main(String[] args) {
C c = F.newThing();
I i = F.newThing();
}
}

Can you require multiple types at once?

Basically I want to do this:
public interface A {
void a();
}
public interface B {
void b();
}
public class SomeClass {
public SomeClass(<A&B> e) { // Note the type here
e.a();
e.b();
}
}
What I did on the commented line is obviously illegal. I know I can just require the passed object to implement interface A, or interface B, but is there a way to do both?
I guess there are workarounds (like requiring the parameter to be of type A and then check if it is also an instanceof B), but that way I don't get help from the compiler. Do you know of any way to do this? Or maybe a smarter workaround...
You can do it with generics enabled. For example, to accept an instance of some class that implements both CharSequence and Appendable:
public <T extends CharSequence & Appendable> void someMethod(T param) {
...
}
Depending on the design, you can do one of the following:
Make A : B or B : A.
Make an interface C : A, B.
Either way you'll want to have contract that includes both a() and b().
Well, there is the <T extends A & B> f(T ab) notation, but you should favour composition over inheritance. You don't really have to extend anything. Just make a type that is the joint union (product) of both types A and B, as follows:
public abstract class P2<A, B> {
public A _1();
public B _2();
}
Sometimes called a product-2, or a "pair" type. You can create a handy constructor for these:
public final class P {
private P() {}
public static <A, B> P2 p(final A a, final B b) {
return new P2<A, B>() {
public A _1() {
return a;
}
public B _2() {
return b;
}
}
}
}
Note that you can use the same object for both arguments, if A and B are interfaces and your object implements both of them:
P2<A, B> both = P.p(o, o);
Or you're free to use two different objects, making the design nice and decoupled.
You'll find this type, as well as products of up to 8 types, included in the Functional Java library. There's also a type called Either<A, B> that is the disjoint union (sum) of two types, such that it holds a value that is of either type A or B (or both).
Just shooting in the dark, I don't know if this is the correct syntax, ie if you have to redeclare the methods in C but what about this:
public interface A {
void a();
}
public interface B {
void b();
}
public interface C extends A, B{}
public class SomeClass{
public SomeClass(C e) { // Note the type here
e.a();
e.b();
}
}
If you have a method that needs two different interfaces as parameters, just make it take two parameters.
public void foo(A a, B b) {
....
}
It’s not that hard, believe me.

Categories

Resources