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.
Related
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.
No, this question is not about the difference between ? and T; it is about how I turn a < ? > argument into a named < T >.
Consider this example code:
import java.io.Serializable;
class A<T extends Serializable> {
<S extends Serializable> void bar(S arg) { }
void bar2(T arg) { }
}
public class B {
A<? extends Serializable> myA = null;
<T extends Serializable> void foo(T arg) {
myA.bar(arg);
myA.bar2(arg);
}
}
My problem is the fact that the above doesn't compile; the call to bar2() gives me
The method bar2(capture#2-of ? extends Serializable) in the type
A<capture#2-of ? extends Serializable> is not applicable for the arguments (T)
I guess the "reason" for that is that myA is a < ? extends Serializable >; but the T in B.foo ... is well, a named T; and not the wildcard ?.
One way to "fix" this would be to make < T extends Serializable > a type parameter for class B ... but that basically conflicts with my other usage of that class. Meaning: I ended up writing down B and its member myA like this - exactly because I don't want to parameterize the B class. So, I started with only the bar2() method; and then added bar() later on ... but that doesn't really help either.
So - is there a way to for my class B to use A.bar2() as "intended" in my example code?
EDIT: and just to be precise - the bar() method doesn't help me; as the "S" there ... isn't compatible with the "T" that I am really using in my A class.
You're in a dead end.
A<? extends Serializable> myA = ...;
So myA is a A of something unknown. It could be a A<String>, or a A<Integer> or a A<Banana>. You just don't know. Let's say it's a A<Banana>.
<T extends Serializable> void foo(T arg) {
So foo() accepts any serializable: String, Integer, Banana, Apple, anything. Let's say it's called with an Integer as argument.
myA.bar2(arg);
So, based on the assumptions above, that would call bar2() with an Integer as argument, although myA is a A<Banana>. That can't be accepted by the compiler: it's clearly not type-safe. B must be made generic.
#JBNizet already explains why this can't be done. This answer aims to explain the possible options you have (one of them being rightly pointed out by in your question but that's not the only option).
Make B take a type parameter
class B<T extends Serializable> {
A<T> myA = null;
void foo(T arg) {
myA.bar(arg);
myA.bar2(arg);
}
}
Make B extend from A (If B passes the is-a test for A)
class B extends A<String> {
public void foo(String arg) {
bar2(arg);
bar(1);
}
}
The difference between the two options is that in 1) both bar and bar2 need to be passed the same type where as in 2) bar and bar2 can be passed different types since bar2 is bound by the type parameter declared for A where as bar is bound by the type parameter declared for the method.
So my problem is this. I am using java and I am trying to do something like this (I am working offline so it is harder for me to give code examples but if needed I will) :
Class A - abstract class
Class B - abstract class with list<A> as property
Class C - extends class A
Class D - extends class B
and in this class, in the constructor I am trying to create the list which is in the B properties with a new object from class C.
The error is: Type mismatch: cannot convert from C to A.
I can't seem to make it work. Any ideas why?
Edit:
abstract public class A {
public int theInt;
}
abstract public class B {
public List<A> theList;
}
abstract public class C extends A {
}
abstract public class D extends B {
public D(){
this.theList = new ArrayList<C>();
}
}
This is the code and I have a compiliation error like I mentioned.
Initialize it as
List<A> list= new ArrayList<A>();
That way you would be able to add any subtype of A
If you want to assign ArrayList<C> to as it appears from the comment
Do it this way
list.AddAll(arrayListC);
ArrayList<C> does not extend List<A>. Therefore the assignment
this.theList = new ArrayList<C>();
is invalid.
You can fix this by adding a type parameter to B
abstract public class B<T extends A> {
public List<T> theList
}
abstract public class D extends B<C> {
public D(){
this.theList = new ArrayList<C>();
}
}
You could even add a type parameter to D:
abstract public class D<T extends A> extends B<T> {
public D(){
this.theList = new ArrayList<T>();
}
}
Alternative:
Just use ArrayList<A> instead of ArrayList<C>.
Please note that lst.add(x); will not compile for the following type combinations:
type of lst | type of x
============================|================================
List<? extends A> | A
----------------------------|--------------------------------
List<C> | E extends A but not C
----------------------------|--------------------------------
List<? extends A> | E extends A
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.
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();
}
}