In this question, I saw that I can use a helper method to 'capture' the wildcard generic into a type T to do type safe operations, like so:
void foo(List<?> i) {
fooHelper(i);
}
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(0));
}
But when I try to do that with the extends keyword, it doesn't work:
void bar() {
barHelper(String.class); //works fine
}
void bar(Class<? extends Comparable<?>> clazz) {
barHelper(clazz); //doesn't compile
}
<T> void barHelper(Class<? extends Comparable<T>> clazz) { }
I get the following error:
The method fooHelper
(Class<? extends Comparable<T>>)
in the type Test is not applicable for the arguments
(Class<capture#1-of ? extends Comparable<?>>)
Is there a way to capture a wildcard when using the extends keyword?
My background is, I have a list of classes that extends a given class A, each one of them with a different generic argument T. For each class, I want to get a reference to its T class, and I was trying to do it type safely.
I think generic constraints must be where the type parameter <T> is declared, so instead of
<T> void barHelper(Class<? extends Comparable<T>> clazz) { }
I would write
<A, B extends A> void barHelper(Class<B> clazz) { }
Or if the super/top class is known
<T extends MySuperType> void barHelper(Class<T> clazz) { }
Related
Here's a visual of the problem:
As can be seen from the visual, the IDE is showing a compile-time error to which it does not allow the class to be inserted into the Map.
Here's a simplified version:
#Override
public <T extends Comparable> void transactPersistentEntityStore(...) {
Map<Class<T>, ComparableBinding> propertyTypeMap = new HashMap<>();
propertyTypeMap.put(EmbeddedArrayIterable.class, EmbeddedEntityBinding.BINDING);
propertyTypeMap.put(EmbeddedEntityIterable.class, EmbeddedEntityBinding.BINDING);
// ...
}
Even if both EmbeddedArrayIterable and EmbeddedEntityIterable implements Comparable
Am I missing or misunderstanding something on generics?
You can simplify the point of the problem to this code snippet:
public <T extends Comparable> void m1(T x) {
Class<? extends Comparable> x1Class = x.getClass();
Class<T extends Comparable> x2Class = x.getClass();
}
Or even to this:
public <T> void m2(T x) {
Class<?> x1Class = x.getClass();
Class<T> x2Class = x.getClass();
}
The line with the variable x2Class has an error in these methods.
This is because the compiler throws away the Generics and thus there is no type T at runtime. T is not reifiable. You cannot obtain the type T at runtime.
Have also a look at this article: Why following types are reifiable& non-reifiable in java?
I have just started learning about Generic.Here i'm trying to set value of global obj to the local obj's value.But i'm getting type conversion error.What is the cause for this error?
class GenUpperBound<T>
{
T obj;
public <T extends Number> void set(T obj)
{
this.obj=obj;
}
public static void main(String...q)
{
GenUpperBound<Integer> w=new GenUpperBound<>();
w.set(10);
}
}
Here is the error....
GenupperBound.java:6: error: incompatible types: T#1 cannot be converted to T#2
this.obj=obj;
^
where T#1,T#2 are type-variables:
T#1 extends Number declared in method <T#1>set(T#1)
T#2 extends Object declared in class GenUpperBound
1 error
The constructor-scoped type-parameter <T extends Number> hides the class-scoped type-parameter <T>. Those two T's do not represent the same type.
This is why the compiler rejects to compile your code, because the argument in the constructor could be potentially of a different type than the type, by which the class is parameterized. For example:
new GenUpperBound<String>(new Integer(1));
The difference can be clearly seen if you change the constructor-scoped type-parameters' name to U:
class GenUpperBound<T> {
T obj;
public <U extends Number> void set(U obj) {
this.obj=obj;
}
...
}
Now this compiles fine, too, but T and U clearly represent different types.
You could fix this problem with:
class GenUpperBound<T extends Number> {
T obj;
public void set(T obj) {
this.obj=obj;
}
...
}
Now there isn't a second T type-parameter, but the constructor uses the class-scoped one. In this case statements like new GenUpperBound<String>(new Integer(1)); will not compile, because the argument is not of the same type as the one that the instance is parameterized with.
Try this:
class GenUpperBound<T extends Number>
{
T obj;
public void set(T obj)
{
this.obj=obj;
}
public static void main(String...q)
{
GenUpperBound<Integer> w=new GenUpperBound<>();
w.set(10);
}
}
You have to declare your class like
class GenUpperBound<T extends Number>
because otherwise your obj could for example be of type String and you can't assign a Number to a String.
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
}
}
this is a piece of code i'm struggling with.
public class Channel<T extends Something>{
public Channel(){}
public void method(T something){}
}
public class Manager{
private static ArrayList<Channel<? extends Something>> channels
= new ArrayList<Channel<? extends Something>>();
public static <T extends Something> void OtherMethod(T foo){
for(Channel<? extends Something> c : channels)
c.method(foo); // this does not work
}
}
The line that does not work gives me compiler error:
The method method(capture#1-of ? extends Something) in the type Channel<capture#1-of ? extends Something> is not applicable for the arguments (T)
I don't understand this error. If I remove all the generics in Manager class it is working but type unsafe.
How should I do this in correct Java?
You need a type parameter for your method public <T extends Something> void method(T foo)
public class Channel<T extends Something> {
public Channel() {
}
public <T extends Something> void method(T foo) {
}
}
public class Manager {
private static ArrayList<Channel<? extends Something>> channels = new ArrayList<Channel<? extends Something>>();
public static <T extends Something> void OtherMethod(T foo) {
for (Channel<? extends Something> c : channels)
c.method(foo); // this does not work
}
}
That's inherently unsafe.
What happens if you add a Channel<MyThing> to the list, then call OtherMethod() with a YourThing?
You should make the entire class generic (and make the members non-static), and use the same T for the channels and the parameter.
I am with a sort of trouble when using java generics in the visitor pattern.
My code is something like that:
public interface MyInterfaceVisitor<A, B> {
public A visitMyConcreteObject(MyConcreteObject object, B parameter);
}
public interface MyObject {
public <A, B> A accept(MyInterfaceVisitor<A, B> visitor, B parameter);
}
public class MyConcreteObject implements MyObject {
#Override
public <A, B> A accept(MyInterfaceVisitor<A, B> visitor, B parameter) {
return visitor.visitMyConcreteObject(this, parameter);
}
}
public class MyConcreteVisitor implements MyInterfaceVisitor<????> {
#Override
public <X extends C> X visitMyConcreteObject(MyConcreteObject object, Class<X> parameter) {
// Do a lot of things.
// Return an instance of the given class.
}
// This method is the entry point of the MyConcreteVisitor.
public <X extends C> void someOtherMethod(Class<X> parameter) {
MyObject m = ...;
X x = m.accept(this, parameter);
...;
}
}
public class C {}
public class Dog extends C {}
public class Cat extends C {}
public class Client {
public static void main(String... args) {
MyConcreteVisitor v = new MyConcreteVisitor();
v.someOtherMethod(Cat.class);
v.someOtherMethod(Dog.class);
}
}
// We have other implementations of the visitor that does not matters, like this one.
public class SomeOtherConcreteVisitor implements MyInterfaceVisitor<String, Integer> {
#Override
public String visitMyConcreteObject(MyConcreteObject object, Integer parameter) {
return "foo";
}
}
I need to find what is the generic signature in the ???? that makes the code compilable allowing the overriden method in MyConcreteVisitor class to match the signature in MyInterfaceVisitor interface.
I can't change the signature of the visitMyObject in the MyInterfaceVisitor interface, nor its generics. This happens because others implementations of MyInterfaceVisitor exists and their generics have nothing to with the ones from MyConcreteVisitor.
The MyConcreteVisitor class should not have a generic per-se, so the compiler must allow a MyConcreteVisitor v = new MyConcreteVisitor(); without generating the unchecked or rawtypes warning.
If I change the concrete visitMyObject to public C visitMyObject(MyObject object, Class<? extends C> parameter) and declare the ???? as <C, Class<? extends C>>, I would need to add a cast in the someOtherMethod.
How to define the generic type making it compilable without getting the unchecked or rawtypes warning, changing the interface or adding a cast? Is this even possible in java or I am abusing the generics too much?
The issue is that your implementation is trying to introduce another type parameter X extends C to the method visitMyConcreteObject and resolve the B parameter with it. You can't make visitMyConcreteObject generic with X but try to resolve B with a type parameterized by X, e.g. Class<X>, because B is resolved at the class declaration but X is only declared by a method of the class.
From what I can see, you have two options. Either make MyConcreteVisitor generic on X:
public class MyConcreteVisitor<X extends C> implements MyInterfaceVisitor<X, Class<X>> {
#Override
public X visitMyConcreteObject(MyConcreteObject object, Class<X> parameter) {
// Do a lot of things.
// Return an instance of the given class.
}
}
Or get rid of X and lose type safety (beyond the concrete type C):
public class MyConcreteVisitor implements MyInterfaceVisitor<C, Class<? extends C>> {
#Override
public C visitMyConcreteObject(MyConcreteObject object, Class<? extends C> parameter) {
// Do a lot of things.
// Return an instance of the given class.
}
}
i think this is what you are looking for:
public class MyConcreteVisitor implements MyInterfaceVisitor<Object,Class<?>> {
#Override
public Object visitMyConcreteObject(MyConcreteObject object, Class<?> parameter) {
// Do a lot of things.
// Return an instance of the given class.
}
// This method is the entry point of the MyConcreteVisitor.
public <X> void someOtherMethod(Class<X> parameter) {
MyObject m = ...;
X x = parameter.cast(m.accept(this, parameter));
...;
}
}