Am having Class A ,Interfaces B,C . aA extends A,aB implements B,cC implements C.But if I try to use MultipleBounded Generics.It is showing compilation error using jdk 1.7.
Please help me to correct the errornous. LINK FOR REFERENCE
public class MultipleBounds {
public static void main(String args[])
{
D d1 = new D();
d1.print(new bB()); // compilation Error
d1.print(new cC()); // compilation Error
d1.print(new aA()); // It works no Issues
}
}
class A {
public void method() {
System.out.println("methodA");
}
}
interface B {
public void method();
}
interface C {
public void method();
}
class aA extends A {
public void method() {
System.out.println("methodaA");
}
}
class bB implements B {
public void method() {
System.out.println("methodbB");
}
}
class cC implements C {
public void method() {
System.out.println("methodcC");
}
}
class D<T extends A & B & C>
{
public void print(T t)
{
t.method();
}
}
Since you are using raw type D for invoking the method, all the generic information from the class is lost. And the method print will be replaced by it's erasure.
Now, the erasure of the type parameter T is the left-most bound it has, which is A. This is evident in JLS §4.6:
The erasure of a type variable (§4.4) is the erasure of its leftmost bound.
So, what compiler sees is this:
class D {
public void print(A t)
{
t.method();
}
}
That is why, it works for A and not for B and C. Lesson: Avoid using raw types.
The problem is here: D d1 = new D();. You are using a raw type, therefore losing the generics information. And the type erasure of a type intersection A & B & C is its first component (A). So your d1 can only accepts As.
For example, if you reorder it to B & A & C, you will only be able to pass Bs.
Finally note that you would not be able to declare your D using generics because no class in your code is an A and a B and a C. You could create a class ABC extends A implements B, C and use D<ABC> d = new D<>();
Related
-Project Classes-
Class A:
public class A {
public static void main(String[] args) {
D d = new D();
B.set(d);
d.add(new H()); //$ (correct error)
B.get().add(new H()); //! (no error)
}
}
Class B:
public class B {
protected static C c;
public static C get() {
return B.c;
}
public static void set(C c)
B.c = c;
}
Class C:
public abstract class C<T> {
protected ArrayList<T> array = new ArrayList<T>();
public void add(T element) {
this.array.add(element);
}
}
Class D:
public class D extends C<G> {
//
}
Class E:
public class E extends C<H> {
//
}
Class F:
public abstract class F<T> {
//
}
Class G:
public class G extends F<X> {
//
}
Class H:
public class H extends F<Y> {
//
}
-Class Roles-
A is the main class
B is a class that stores an object reference
C is a generic class that defines an array of objects
D & E extend C with different types
G & H extend F with different types
-Logic Issue-
In A.main(), //! represents where the issue lies and //$ represents the desired behavior. Because B.get() returns abstract type C, adding an illegal element to array is able to be done (in this example, adding H when only G is legal due to D's extension of C).
The type of the returned object from B.get() is not known in the program (because B.c can represent an object of either type D or type E), so a modification to B to check c's type and return it or automatically cast it in the call of B.get() is ideal, if possible.
-Question Edits-
For clarification, casting with ((D) B.get()).add(new H()); is unhelpful as the child type that B.c holds is unknown. If it is possible to do something similar to ((B.get().getClass()) B.get()).add(new H()), that would be helpful. The solution I would opt for would preferably be a change of or in the method B.get() rather than in the main method, if possible.
For further clarification, the outcome of solving this issue is supposed to result in an error as to prevent undesired adding of illegal types to arrays when referenced from B.get() (//! represents where no error occurs and //$ represents where the error occurs as it should). I am open to re-writing how to save references to instances of D and E in B if a dynamic way of referring to the type of C can be done (type D or type E) can be done.
Generic types only work at compile time. It sounds like you're trying to make generics do dynamic type-safety, which is something they are terrible at doing because of erasure. Using B as a singleton whose static c has ever-changing type is incredibly awkward. You could attempt to retain a mapping from Class<?> to C<?> in B and fetch the appropriate one before trying to call add, I guess.
Also, at //$ you simply used the wrong subclass.
d.add(new G());
type-checks correctly.
Here's what I meant:
public class A {
public static void main(String[] args) {
D d = new D();
B.put( G.class, d);
B.get( H.class ).add(new H()); // dynamic typing, but susceptible to null as written
d.add(new G()); //$ (no longer causes error)
}
}
public class B {
protected static Map< Class<?>, C<?> > mapC;
public static <T> C<T> get( Class<T> clsT ) {
return ( C< T > )mapC.get( clsT );
}
public static < T > void put(Class< T > clsT, C<T> c) {
mapC.put( clsT, c );
}
}
public abstract class C<Type> {
protected ArrayList<Type> array = new ArrayList<Type>();
public void add(Type element) {
this.array.add(element);
}
}
public class D extends C<G> {
//
}
public class E extends C<H> {
//
}
public abstract class F<Type> {
//
}
public class G extends F<String> {
//
}
public class H extends F<Integer> {
//
}
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.
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 asked a similiar question 10 minutes ago, but pasted the wrong code snippet. I'm really sorry about that.
I'm currently facing an issue with base and subclasses.
While having a single object as parameter (method single) the compiler doesn't complain.
But if it comes to lists the compiler forces me to declare the list as <? extends Base>
After that I'm no longer allowed to add objects of the base type to that list.
The error message: "The method list(List<Generics.Base>) in the type Generics.C is not applicable for the arguments (List<Generics.Sub>)"
public class Generics {
class Base { }
class Sub extends Base{ }
interface I {
public void list( List<Base> list );
public void single( Base list );
}
class C implements I {
public void list( List<Base> b) { }
public void single( Base p) { }
}
void test() {
C c = new C();
c.single( new Sub() );
List<Sub> b = new ArrayList<Sub>();
c.list( b ); // error message as above
}
public static void main( String[] args) {
Generics g = new Generics();
g.test();
}
}
Is there any other way but declaring the list-methods argument as type <? extends Base> ?
Below are the 2 ways to do it....
public void list(List<? extends Base> list){
}
Or
public <T extends Base> void list(List<T> list){
}
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.