Java Generic classes assignability [duplicate] - java

This question already has answers here:
Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
(3 answers)
Closed 8 years ago.
I have the following classes:
public interface ModelObject {
}
public interface Resource {
}
public interface Transformer <F,T>{
}
public interface WrapperFactory {
Transformer<Resource, Wrap<? extends ModelObject>> createMapper();
}
public class Wrap<E extends ModelObject> {
}
public class AbstractBaseTransformer<F,T> implements Transformer<F,T> {
}
public class ConcreteModel implements ModelObject {
}
public class ConcreteTransformer extends AbstractBaseTransformer<Resource, Wrap<ConcreteModel>> {
}
public class ConcreteFactory implements WrapperFactory {
#Override
public Transformer<Resource, Wrap<? extends ModelObject>> createMapper() {
return new ConcreteTransformer();
}
}
The ConcreteFactory doesn't compile stating that ConcreteTransformer is incompatible with returned
Transformer<Resource, Wrap<? extends ModelObject>>
I can't see what's wrong here. ConcreteTransformer binds 1st parameter to Resource (same as expected) while binding 2nd parameter to:
Wrap<ConcreteModel>
which should bind to:
Wrap<? extends ModelObject>
as ConcreteModel implements it.

Here is a simpler version, to narrow down the issue:
interface ModelObject {}
class ConcreteModel implements ModelObject {}
class Wrap<E extends ModelObject> {}
class SomeGeneric<T> {}
class Simple {
public SomeGeneric<Wrap<? extends ModelObject>> m() {
return new SomeGeneric<Wrap<ConcreteModel>>();
}
}
does not compile either.
Your problem is that a SomeGeneric<Wrap<ConcreteModel>> is not a SomeGeneric<Wrap<? extends ModelObject>>.

Wrap<ConcreteModel> is a subtype of Wrap<? extends ModelObject>? Yes.
Transformer<Resource, Wrap<ConcreteModel>> is a subtype of Transformer<Resource, Wrap<? extends ModelObject>>? No.
It's the same as:
String is a subtype of Object? Yes.
List<String> is a subtype of List<Object>? No.
Basically, for parameterized types to be compatible, if the top-level parameter is not wildcard, then the parameters must match exactly. In your case, the top-level parameter is not wildcard, and the parameters don't match exactly.
What you probably wanted instead was
Transformer<Resource, ? extends Wrap<? extends ModelObject>>

A Wrap<ConcreteModel> can be assigned to a variable of type Wrap<? extends ModelObject>. But the matter here is more complex.
Assume you have a ArrayList<Wrap<? extends ModelObject>> list. When you have such a type, it means that you can add a Wrap<ConcreteModel> to the list, but it also means that you can add a Wrap<ModelObject> to it. In brief, it means you have a list, that can contain a Wrap of anything that can be cast to a ModelObject.
On the other side, having a ArrayList<Wrap<ConcreteModel>> list means you can only add Wrap<ConcreteModel>s to it, while a Wrap<ModelObject> cannot be added to it, because the list can only contain wrapped ConcreteModels, and a wrapped ModelObject is not a wrapped ConcreteModel nor it can be cast to be one.
This is exactly your case. You declared your createMapper() method to return a Transformer<Resource, Wrap<? extends ModelObject>>. This means that the second argument of the returned Transformer must be able to be any subclass of ModelObject, including ModelObject itself. On the contrary, you are trying to return a Transformer<Resource, Wrap<ConcreteModel>>.
The compiler needs to enforce this because Transformer<F, T> could declare a method:
void myMethod(F fObject, T tObject);
If that was the case, the method myMethod of an object of type Transformer<Resource, Wrap<? extends ModelObject>> would accept an object of type ModelObject as its second argument. On the other side, the same method, in a object of type Transformer<Resource, Wrap<ConcreteModel>> cannot accept a ModelObject as its second argument.

Related

Java generics incompatible types while subclassing

While sub classing from generic class type/Formal type parameter T/E with valid class type/Actual type parameter say e.g. Type/String there are many combinations occurs and that confusing which one to use and when?
public class SubClass<T> implements SuperIfc<T> <-- It is straight forward to understand
public class SubClass<T> implements SuperIfc<Type>
public class SubClass<Type> implements SuperIfc<T>
public class SubClass<Type> implements SuperIfc<Type>
public class SubClass<Type> implements SuperIfc
public class SubClass implements SuperIfc<Type>
public class SubClass implements SuperIfc<T> <--- Hope we cannot declare <T> in his case while initialising SubClass.
// Bounded type parameter
public class SubClass<T extends Type> implements SuperIfc<Type>
public class SubClass<T extends Type> implements SuperIfc<T> <-- Looks <T> at SuperIfc also refers <T extends Type>, and no need to declare it again at SuperIfc.
// Recursive type bound
public class SubClass<T extends Comparable<T>>> implements SuperIfc<T>
public class SubClass<T extends Comparable<T>>> implements SuperIfc<Type>
So that i can be more clearer on solving incompatible types while subclassing
Case_1:
public class Test {
interface TestIfc {
public static <T extends TestIfc> T of(int choice) {
if(choice == 1) {
return new TestImpl(); <-- PROB_1: incompatible type error
} else {
return new SomeOtherTestImpl(); //incompatible type error
}
}
}
static class TestImpl implements TestIfc {}
static class SomeOtherTestImpl<T extends TestIfc> implements TestIfc {
//The below method also having same error though with declaration
public T of() {
return new TestImpl(); <-- PROB_2: incompatible type error
}
}
}
Case_1: PROB_1: return type is T extends TestIfc and returned TestImpl implements TestIf So what is wrong?
Case_1: PROB_2: Similar to PROB_1, how to rectify without external casting. Please help.
Case_2:
public interface SuperIfc<T> {
public T create(Object label);
}
class Type {
public static Type of(){
return new Type();
}
}
------
public class SubClass<Type> implements SuperIfc<Type>{
#Override
public Type create() {
return Type.of(); <---- PROB_1: cannot resolve method
}
}
-------
public class SubClass<T extends Type> implements SuperIfc<Type>{
#Override
public Type create() {
return Type.of(); <---- PROB_1: is resolved
}
}
SuperIfc<Type> object = new SubClass(); <-- PROB_2 Unchecked assignement warning
SuperIfc<Type> object = new SubClass<TypeImpl>(); <-- PROB_3: bound should extend Type
I would like to know how to resolve Case_2, PROB_1 and PROB_2 together?
How to write subclass for generic super class with class types and what are the rules?
What should be taken care when changing generic T to class Type while subclassing? may be the difference between below and when to use?
public class SubClass<Type> implements SuperIfc<Type>
public class SubClass<Type> implements SuperIfc
public class SubClass implements SuperIfc<Type>
public class SubClass<T extends Type> implements SuperIfc<Type>
public class SubClass<T extends Type> implements SuperIfc<T>
public class SubClass<T> implements SuperIfc<Type>
In the first of() method, the method can return any type that implements InformationIfc, but your method always returns a specific implementation - InformationImpl - which is not acceptable.
For example, if you had some other class SomeOtherInformationImpl that implements that interface, the caller of that method would be allowed to write:
SomeOtherInformationImpl i = InformationImpl.of();
but your method doesn't return a SomeOtherInformationImpl.
The second of() method has the same issue as the first method.
If you instantiated your class with:
InformationImpl i = new InformationImpl<SomeOtherInformationImpl>();
the of() method would have to return a SomeOtherInformationImpl, not a InformationImpl.
Problems with case one.
PROB_1: return type is T extends TestIfc
Why do you have a generic here at all?. Since you have a static method I can do.
TestIfc broken = TestIfc<SomeOtherImplementation>.of(0);
SomeOtherImplementation is not a TestImpl. This is broken by design. What you really want is.
public static TestIfc of(int choice)
Next.
static class SomeOtherTestImpl<T extends TestIfc> implements TestIfc {
TestIfc is not parameterized, SomeOtherTestImp is, but it is completely unrelated to the interface you're implementing. Not to mention, TestIfc has a static method of that has nothing to do with the interface.
If I had to guess, I would think you want.
interface TestIfc<T>{}
static class TestImpl implements TestIfc<TestImpl> {}
static class SomeOtherTestImpl<T extends TestIfc> implements TestIfc<T>{}
That is the best I could come up with, because it is unclear what you actually want to happen.
Your examples for question 3
public class SubClass<Type> implements SuperIfc<Type>
This is broken, because SubClass<Type> declares Type to be the name of a generic parameter. It puts no restriction on the type, hence you get the method not found error.
public class SubClass<Type> implements SuperIfc
Broken, makes a generic parameter named Type, has nothing to do with your raw type version of SuperIfc
public SubClass implements SuperIfc<Type>
This is good.
public class SubClass<T extends Type> implements SuperIfc<Type>
public class SubClass<T> implements SuperIfc<Type>
These are both good, but the T has no relation to the SuperIfc parameter, hence your implementation would be.
public Type create(Object label);
The first generic parameter says the name you're going to use through the class.
This is a long answer, but please read through it (at least case 1 and the very end, you can skip over problem 2's solution in case 2 if you wish)
Case 1
Problem 1:
Your problem here is that the compiler cannot prove that T is the same as TestImpl or SomeOtherTestImpl. What if there was another class, call it TestFoo, that implemented TestIfc? Then, if you called the method as TestIfc.<TestFoo>of(1), you would expect an object of type TestFoo, but you would instead get a TestImpl, which is wrong. The compiler doesn't want you doing that, so it throws an error. What you should do is just remove the generics, like so:
public static TestImpl of(int choice) {
if(choice == 1) {
return new TestImpl();
} else {
return new SomeOtherTestImpl();
}
}
And then you can safely call TestIfc.of.
Problem 2:
This is basically the same thing. The compiler can't prove that the type parameter T of SomeOtherTestImpl is the same as TestImpl. What if you have an object like this (where TestFoo does not extend TestImpl but implements TestIfc)?
SomeOtherTestImpl<TestFoo> testImpl = ...;
And then you try to call the of method like this:
TestFoo testFoo = testImpl.of();
Can you see the problem here? Your of method returns a TestImpl, but you expect it to return a TestFoo because T is TestFoo. The compiler stops you from doing that. Again, you should just get rid of the generics:
public TestImpl of() {
return new TestImpl(); //This works now
}
Case 2
Problem 1
This problem is caused simply because you named your type parameter the same thing as your class - Type. When you changed the name of your type parameter to, say, T, it'll work because now Type is the name of a class. Before, your type parameter Type was hiding the class Type.
However, again, there is no need for a type parameter, since, you can just do this, and it'll satisfy all constraints.
public class SubClass implements SuperIfc<Type> {
#Override
public Type create() {
return Type.of();
}
}
Problem 2:
The unchecked assignment is because you didn't provide type arguments to SubClass when you did
SuperIfc<Type> object = new SubClass();
This can be fixed by either explicitly giving Type to SubClass:
SuperIfc<Type> object = new SubClass<Type>();
or by putting in an empty diamond (which I much prefer). This second approach means that the compiler can infer by itself that by new Subclass<>(), you mean new Subclass<Type>().
SuperIfc<Type> object = new SubClass<>();
Why the compiler complains:
That constructor call (new Subclass()) is basically like calling a method that looks like public SubClass makeNewSubClass() {...}. In fact, let's replace that statement above with this one, which will cause the same warning.
SuperIfc<Type> object = makeNewSubClass();
SubClass normally takes 1 type parameter (call it T), but here, it's not given any type parameters. That means that T could be anything, any class that extends Type (because of the constraint SubClass<T extends Type>).
You might thinking that if T is always going to be a subclass of Type, it should work all right, because the type of object above is SuperIfc<Type>. Assume there's a class like this - class TypeFoo extends Type - and that the method makeNewSubClass actually returns an object of type SubClass<TypeFoo> (new SubClass<TypeFoo>()).
Because of this, you expect object to be SuperIfc<Type> but it's actually SubClass<TypeFoo>. You probably think that that's all right, because after all, TypeFoo is a subclass of Type, right? Well, Java's generics are actually invariant, which means that for the compiler, SubClass<TypeFoo> is not a subclass of SuperIfc<Type> - it thinks they're 2 completely unrelated types. Don't ask me - I have no idea why :). Even SubClass<TypeFoo> isn't considered the same as or a subclass of SubClass<Type>!
That's why the compiler emits a warning - because raw types (that's what you call it when you have new SubClass() without giving type arguments) could represent anything. As for why it emits a warning and not an error - generics were introduced in Java 5, so for code before that to compile, the compiler lets you off with just a warning.
Problem 3:
According to the error, the type argument you gave (TypeImpl) should extend Type. This is because you defined SubClass as class SubClass<T extends Type> .... Here, you are giving TypeImpl as an argument for T, and so TypeImpl must extend Type, so all you need to do is put do class TypeImpl extends Type to solve that particular error.
However, even after you do that, you'll get an "incompatible types" error because, as I said earlier when talking about Problem 2, SuperIfc<Type> is not considered a supertype of SubClass<TypeImpl> even if TypeImpl extends Type.
You can do this
SuperIfc<TypeImpl> object = new SubClass<TypeImpl>();
or you can do this
SuperIfc<? extends Type> object = new SubClass<TypeImpl>();
In the second solution, we lose the information of what exactly T is in SuperIfc<T>; all we know is that it extends Type. The benefit of the second one is that you can later reassign any SubClass object to it (which doesn't work with the first version because it only allows SubClass<TypeImpl>:
SuperIfc<? extends Type> object = new SubClass<TypeImpl>();
object = new SubClass<Type>(); //works great
Alternative solution for Case 2
What it seems like you want to do is return objects of different type depending on type parameters. That's not really possible to do, since type parameters are erased at runtime. There are workarounds, though.
Here's one way to do it with a functional interface. It involves no casting and ensures type safety.
//This is the functional interface. You can also use Supplier here, of course
interface Constructor<T> {
T create();
}
class SubClass<T extends Type> implements SuperIfc<T> {
public Constructor<T> ctor;
public SubClass(Constructor<T> ctor) {
this.ctor = ctor;
}
#Override
public T create(Object label) {
return ctor.create();
}
}
class Type {}
class TypeImpl extends Type{}
By using a Constructor object, you will know that the returned Type object will always be of the right type. This is very easy to apply to your existing code - you don't need to write your own ConstructorImpl classes or anything.
You can now write this, and all you need to add is a method reference to the constructor (Type::new).
SuperIfc<Type> object1 = new SubClass<>(Type::new);
SuperIfc<? extends Type> object2 = new SubClass<>(TypeImpl::new);
With lambdas, you can also write this in a slightly more verbose way:
SuperIfc<TypeImpl> object = new SubClass<>(() -> new TypeImpl());
---
SuperIfc<? extends Type> object = new SubClass<>(() -> new TypeImpl());

Is it possible to make generic method signature implementing generic + interface? [duplicate]

This question already has answers here:
Why can't I use a type argument in a type parameter with multiple bounds?
(5 answers)
Closed 7 years ago.
Is it possible to use generics something like that in Java 1.7?
class Test<T> {
public <Z extends T & Serializable> void method(Z test) {
...
}
}
I would like my method to accept only objects of generic type which implements specific interface.
No, unfortunately it is not possible to use generic extends with a generic type and an interface. In fact, it is not even possible to use generic extends with multiple types. If you could, then you could do something like the following.
class Test<T, B extends Serializable> {
public <Z extends T & B> void method(Z test) {
...
}
}
This restriction against extending multiple types may be because of type erasure. At runtime the generics are removed and public <Z extends Serializable> simply becomes public Serializable. So what would <Z extends T & Serializable> be replaced with?
The most approximated form would be:
class Test<T extends Serializable>
{
public <Z extends T> void method(Z test)
{
}
}

How to pass in a class name in to abstract superclass constructor?

This is based off my last question
Why am i getting a class cast exception(with generics, comparable)?
Here is my design again.
I have an abstract super class, AbstractArrayList, and two concrete subclasses that extend it, sorted and unsorted array list.
Here's AbstractArrayList which manages the actual data because it needs to for the already implemented methods.
public abstract class AbstractArrayMyList<E> implements MyList<E> {
protected E[] elementData;
.....
}
Here is the declaration for ArrayListSorted, which extends AbstractArrayMyList
public class ArrayListSorted<E extends Comparable<E>> extends AbstractArrayMyList<E>
And the lines of test code that caused the exception
ArrayListSorted<Integer> toTestInteger = new ArrayListSorted<Integer>()
toTestInteger.insert(0);
assertEquals(toTestInteger.get(0).intValue(), 0);
And the actual exception itself
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;
at myarraylist.ArrayListSorted.getIndex(ArrayListSorted.java:38)
ArrayListSorted - java line 38 is inside this method
public int getIndex(E value) {
....
line 38 - if (value.compareTo(elementData[mid]) < 0) hi = mid - 1;
The response I got from the question clarified what the exception was caused by.
When I tried making this call
value.compareTo(elementData[mid]) < 0
value was of the correct type because the extends part will narrow the type object to Comparable type. However the JVM which runs the java bytecode, will only recognize the elementData array as an object array so when I am trying to cast elementData[mid] to Comparable, it's not actually of Comparable type.
The solution that chrylis gave me in the other question was to have a constructor in the AbstractArrayList that will construct the right typed array
protected AbstractArrayMyList(Class<E> clazz) {
this.elementClass = clazz;
this.elementData = Array.newInstance(clazz, INITIAL_SIZE);
}
I am trying to call this constructor in my sorted array list subclass, with
public class ArrayListSorted<E extends Comparable<E>> extends AbstractArrayMyList<E>
public ArrayListSorted() {
super(Class.forName(("E"));
}
...
Syntax I got from this thread - Passing a class as an argument to a method in java
However I get a compiler error - The constructor AbstractArrayMyList(Class) is undefined.
Does anyone know what the issue is? I defined that same constructor that chrylis provided me in my question in AbstractArrayList that takes in a class instance.
If you need the concrete class for a Parameter, you have to specify it. There is no way to derive it from the type parameter E.
This applies to your ArrayListSorted() constructor. If you want to keep ArrayListSorted generic (with a type Parameter), then you can only pass the Class object along. If you want to make it specific, you can just specify the parameter and the class.
In that case I would use a concrete class name like:
public class ArrayListSorted<E extends Comparable<E>>
extends AbstractArrayMyList<E> {
public ArrayListSorted(Class<E> cls) {
super(cls);
}
...
public IntArrayListSorted extends ArrayListSorted<Integer> {
public IntArrayListSorted() {
super(Integer.class);
}
...
As you can see, when you specify the type parameter (... extends ArrayListSorted<Integer>) it will be defined as Integer, and then the constructor also expects a Class<Integer>: (super(Integer.class)).
One option is, to use a common interface as the implementation, so you can have:
public class ArrayListSorted<E extends Comparable<E>> extends AbstractArrayMyList<Comparable>
{
public ArrayListSorted()
{
super(Comparable.class);
}
}
No, not literally E, but the class you're using for E. See the constructor for EnumMap:
new EnumMap<>(KeyEnum.class)
You get the error "The constructor AbstractArrayMyList(Class) is undefined"
because you don't have such a constructor.
public AbstractArrayMyList(Class<E> clazz)
{
elementData=(E[])Array.newInstance(clazz);
}

Passing generic subtype class information to superclass in Java

I've long used an idiom in Java for using the class information of a (non-abstract) class in the methods of its (generally abstract) ancestor class(es) (unfortunately I can't find the name of this pattern):
public abstract class Abstract<T extends Abstract<T>> {
private final Class<T> subClass;
protected Abstract(Class<T> subClass) {
this.subClass = subClass;
}
protected T getSomethingElseWithSameType() {
....
}
}
An example of a subclass thereof:
public class NonGeneric extends Abstract<NonGeneric> {
public NonGeneric() {
super(NonGeneric.class);
}
}
However, I'm having trouble defining a subclass of Abstract which has its own generic parameters:
public class Generic<T> extends Abstract<Generic<T>> {
public Generic() {
super(Generic.class);
}
}
This example is not compilable; likewise, it is not possible to specify the generic types using e.g. Generic<T>.class or even to use a wildcard like Generic<?>.
I also tried replacing the declaration of generic type T in the superclass to ? extends T, but that isn't compilable either.
Is there any way I can get this pattern to work with generic base classes?
The "pattern" (idiom) of passing an instance of Class<T> (typically to the constructor) is using Class Literals as Runtime-Type Tokens, and is used to keep a runtime reference to the generic type, which is otherwise erased.
The solution is firstly to change the token class bound to:
Class<? extends T>
and then to put a similar requirement on your generic subclass as you did with your super class; have the concrete class pass a type token, but you can type it properly as a parameter:
These classes compile without casts or warnings:
public abstract class Abstract<T extends Abstract<T>> {
private final Class<? extends T> subClass;
protected Abstract(Class<? extends T> subClass) {
this.subClass = subClass;
}
}
public class NonGeneric extends Abstract<NonGeneric> {
public NonGeneric() {
super(NonGeneric.class);
}
}
public class Generic<T> extends Abstract<Generic<T>> {
public Generic(Class<? extends Generic<T>> clazz) {
super(clazz);
}
}
And finally at the concrete class, if you declare the usage as its own class, it doesn't require a cast anywhere:
public class IntegerGeneric extends Generic<Integer> {
public IntegerGeneric() {
super(IntegerGeneric.class);
}
}
I haven't figured out how to create an instance of Generic (anonymous or not) without a cast:
// can someone fill in the parameters without a cast?
new Generic<Integer>(???); // typed direct instance
new Generic<Integer>(???) { }; // anonymous
I don't think it's possible, but I welcome being shown otherwise.
The major problem you have got here is, there is no class literal for concrete parameterized type. And that makes sense, since parameterized types don't have any runtime type information. So, you can only have class literal with raw types, in this case Generic.class.
Reference:
Java Generics FAQs
Why is there no class literal for concrete parameterized types? :
Well, that's fine, but Generic.class gives you a Class<Generic> which is not compatible with Class<Generic<T>>. A workaround is to find a way to convert it to Class<Generic<T>>, but that too you can't do directly. You would have to add an intermediate cast to Class<?>, which represents the family of all the instantiation of Class. And then downcast to Class<Generic<T>>, which will remove the compiler error, though you will an unchecked cast warning. You can annotate the constructor with #SuppressWarnings("unchecked") to remove the warning.
class Generic<T> extends Abstract<Generic<T>> {
public Generic() {
super((Class<Generic<T>>)(Class<?>)Generic.class);
}
}
There is no need in Class<T> subClass argument. Change:
protected Abstract(Class<T> subClass) {
this.subClass = subClass;
}
to:
protected Abstract(Class subClass) {
this.subClass = subClass;
}
and everything will compile.

Is there any better solution for abstract class A<T extends A>?

I want to override a method and replace a parameter it takes with a subclass of that parameter.
For return type there is no problem, because they are not a part of method signature and can be replaced with subclasses (called "covariant return type"). For arguments this is not working, because they are a part of signature.
So I came out with solution to use generic parameter of the same type:
public abstract class A<T extends A> {
public void loadValuesFrom(T source) {
...
}
}
public class B extends A<B> {
public void loadValuesFrom(B source) {
super.loadValuesFrom(source);
...
}
}
But the statement "public abstract class A" looks odd to me. Are there any other ways to achieve this? Any "covariant parameter type"? :-)
If it must be a parameter type, using generics is probably the best option. I'd only do a minor correction to avoid the raw type in the class declaration:
public abstract class A<T extends A<T>> {
If however you have the special case where the object being initialized needs to be freshly created, you might include creation in a method, thereby removing the need to pass that object as parameter:
public abstract class A {
public A clone() {
}
}
public class B extends A {
public B clone() {
// copy state
}
}

Categories

Resources