I have two isomorphic type hierarchies. The base type of the first one is BaseA and the base type of the second one is BaseB. I know how to transform any object of any subclass of BaseB to its corresponding subtype of BaseA. I want to implement a method which takes object of type BaseB determines its class and constructs an object of the corresponding subtype of BaseA. Example code:
public interface BaseA...
public interface BaseB...
public class DerA implements BaseA...
public class DerB implements BaseB...
...
public interface Transform<A,B> {
A toA (B b);
}
public class DerAtoDerB implements Transform<DerA,DerB> {
DerA toA (DerB b){...}
}
public class Transformations {
private static Map<Class<?>, Transform<? extends BaseA, ? extends BaseB>> _map =
new HashMap<>();
static {
_map.put(DerB.class, new DerAtoDerB());
}
public static <B extends BaseB> BaseA transform(B b){
Transform<? extends BaseA, ? extends BaseB> t = _map.get(b.getClass());
return t.toA(b); // Compile error: Transform<A,B#2> cannot be applied to given types
}
Why <B extends BaseB> is not compatible with <? extends BaseB> ? Also if I try implementing the static transform method like this:
public static BaseA transform(BaseB b){
Transform<? extends BaseA, ? extends BaseB> t = _map.get(b.getClass());
return t.toA(b); // Compile error: Transform<A,B> cannot be applied to given types
}
I get a compilation error: Transform<A,B> cannot be applied to given types
Can anyone explain me what I am doing wrong with Generics?
The problem is that in the transform method the compiler can't know that the type parameter B extends BaseB and the second type parameter in the Transform class (? extends BaseB) that was gotten from the map actually represent the same subclass of BaseB. Nothing stops you from storing an incompatible type in the map:
_map.put(DerB.class, new AnotherDerAtoAnotherDerB()); // the types don't match
You are the one who guarantees that the types in the map match, so you need to tell the compiler by casting it to the correct type:
#SuppressWarnings("unchecked")
public static <B extends BaseB> BaseA transform(B b) {
Transform<? extends BaseA, B> t =
(Transform<? extends BaseA, B>)_map.get(b.getClass());
return t.toA(b);
}
When the compiler encounters a variable with a wildcard in its type it knows that there must have been some T that matches what was sent in. It does not know what type T represents, but it can create a placeholder for that type to refer to the type that T must be. That placeholder is called the capture of that particular wildcard.
I don't know why the compiler can't figure out that capture<? extends BaseB> could be capture<?> extends BaseB, maybe something with type erasure?
I would instead implement it like this:
interface BaseA {}
interface BaseB {}
class DerA implements BaseA {}
class DerB implements BaseB {}
interface Transform {
BaseA toA(BaseB b);
}
class DerAtoDerB implements Transform {
public BaseA toA(BaseB b) { return new DerA(); }
}
class Transformations {
private static Map<Class<?>, Transform> _map =
new HashMap<>();
static {
_map.put(DerB.class, new DerAtoDerB());
}
public static<B extends BaseB> BaseA transform(B b) {
Transform t = _map.get(b.getClass());
return t.toA(b);
}
}
? means unknown type.
When a variable is of type X you can assign it a value of type X or any subtype of X but "? extends X" means something else.
It means there is an unknown type that may be X or any subtype of X. It is not the same thing.
Example:
public static Transform<? extends BaseA, ? extends BaseB> getSomething(){
// My custom method
return new Transform<MySubclassOfA, MySubclassOfB>(); // <-- It does not accept BaseB, only MySubclassOfB
}
public static BaseA transform(BaseB b){
Transform<? extends BaseA, ? extends BaseB> t = getSomething();
return t.toA(b); // <--- THIS IS WRONG, it cannot accept any BaseB, only MySubclassOfB
}
In the example the compiler does not know if t admits any BaseB or what but I shown an example where it doesn't.
This thing compiles:
package com.test;
import java.util.HashMap;
import java.util.Map;
interface BaseA{}
interface BaseB{}
class DerA implements BaseA{}
class DerB implements BaseB{}
interface Transform<A,B> {
A toA (B b);
}
class DerAtoDerB implements Transform<BaseA,BaseB> {
public DerA toA(DerB b){ return null; }
#Override
public BaseA toA(BaseB baseB) {
return null;
}
}
public class Transformations {
private static Map<Class<?>, Transform<? extends BaseA, ? super BaseB>> _map = new HashMap<Class<?>, Transform<? extends BaseA, ? super BaseB>>();
static {
_map.put(DerB.class, new DerAtoDerB());
}
public static <B extends BaseB> BaseA transform(B b){
Transform<? extends BaseA, ? super BaseB> t = _map.get(b.getClass());
return t.toA(b);
}
}
The changes I made to your code are the following:
DerAtoDerB now implements Transform<BaseA,BaseB>, instead of Transform<DerA,DerB>
Type of second generic parameter of Map has changed to Transform<? extends BaseA, ? super BaseB> - pay attention to use of super instead of extends - it's the opposite type bound.
Main concept of Java generics: if ChildClass extends ParentClass it DOES NOT mean YourApi<ChildClass> extends YourApi<ParentClass>. E.g.:
NumberTransform<String, ? extends Number> intTransform = new IntegerTransform<String, Integer>(); // work with Integer numbers only
NumberTransform<String, ? extends Number> longTransform = new LongTransform<String, Long>(); // work with Long numbers only
longTransform.toA((Integer) 1); // you are trying to make this and got compilation error.
To help compiler replace your t initialization:
Transform<? extends BaseA, B> t = (Transform<? extends BaseA, B>) _map.get(b.getClass());
Related
The SCCE below shows 2 classes (B and C) implementing the interface Marker. For each class that implements Marker there is a corresponding class implementing the generic Handler interface (B_Handler, C_Handler). A map is used to associate the Class type of Pair.second to it's associated Handler. The code executes as anticipated; however, I get a compile-time warning:
warning: [unchecked] unchecked cast
Handler h1 = (Handler) (dispatch.get(p1.second.getClass()));
required: Handler
found: Handler
where CAP#1 is a fresh type-variable:
CAP#1 extends Marker from capture of ? extends Marker
What's the cleanest way to resolve this besides #SuppressWarnings(value = "unchecked")?
package genericpair;
import java.util.HashMap;
import java.util.Map;
import javax.swing.SwingUtilities;
public class GenericPair
{
public class A
{
}
public interface Marker
{
}
public class B implements Marker
{
}
public class C implements Marker
{
}
public Pair<A, Marker> getTarget()
{
A a = new A();
C c = new C();
return new Pair<>(a, c);
}
public interface Handler<T extends Marker>
{
void handle(Pair<A, T> target);
}
public class B_Handler implements Handler<B>
{
#Override
public void handle(Pair<A, B> target)
{
System.out.println("B");
}
}
public class C_Handler implements Handler<C>
{
#Override
public void handle(Pair<A, C> target)
{
System.out.println("C");
}
}
public class Pair<F, S>
{
public final F first;
public final S second;
public Pair(F first, S second)
{
this.first = first;
this.second = second;
}
}
private void executeSCCE()
{
// register a handler for each Marker type
Map<Class, Handler<? extends Marker>> dispatch = new HashMap<>();
dispatch.put(B.class, new B_Handler());
dispatch.put(C.class, new C_Handler());
// get a target (e.g., Pair<A,C>)
Pair<A, Marker> p1 = getTarget();
// select handler based on the class type of the second parameter
Handler<Marker> h1 = (Handler<Marker>) (dispatch.get(p1.second.getClass()));
h1.handle(p1);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> new GenericPair().executeSCCE());
}
}
Consider the following example:
List<? extends List> test1 = new ArrayList<>();
List<List> test2 = (List<List>) test1;
Here we get the warning:
warning: [unchecked] unchecked cast
List<List> test2 = (List<List>) test1;
^
required: List<List>
found: List<CAP#1>
where CAP#1 is a fresh type-variable:
CAP#1 extends List from capture of ? extends List
This happens because there is no way to ensure that the generic constraint of List<List> will match List<? extends List>. Imagine that we rewrite this example to the following:
List<? extends List> test1 = new ArrayList<ArrayList>();
List<List> test2 = (List<List>) test1;
test1.add(new LinkedList<>());//ERROR no suitable method found for add(LinkedList<Object>)
test2.add(new LinkedList<>());//Will work fine!!
Here it is more obvious that the initial contract is broken. The list defined to contain ArrayList now contains a LinkedList. This is unsafe, and is why you are getting this warning. So there is no way to cast from Handler<? extends Marker> to Handler<Marker> safely.
There are several issues.
The first is that your Map is not able to express the type relationship between each key and its value. So if you pass a Class<T> to dispatch.get(), you only get a Handler<? extends Marker> back, not Handler<T>. In fact, there is no type you can give dispatch to make that work. Instead, you have to make a wrapper class to enforce this relationship via its API:
public class ClassToHandlerMap
{
private final Map<Class<?>, Handler<?>> map = new HashMap<>();
public <T extends Marker> void put(Class<T> clazz, Handler<T> handler) {
map.put(clazz, handler);
}
#SuppressWarnings("unchecked")
public <T extends Marker> Handler<T> get(Class<T> clazz) {
return (Handler<T>)map.get(clazz);
}
}
Note that you do still have to suppress unchecked warnings inside this class, but at least here you know it's provably correct, based on how things are allowed to be put into the map. The unchecked cast is just an implementation detail that the user of this class doesn't need to know about.
The second issue is that getTarget() should probably return Pair<A, ? extends Marker> instead of Pair<A, Marker>. You don't ever have a Handlers of Marker; rather, you have Handlers of particular types of Marker. So it makes sense that you only use Pairs of particular types of Marker too.
public Pair<A, ? extends Marker> getTarget()
{
A a = new A();
C c = new C();
return new Pair<>(a, c);
}
The last part of your function basically is using p1 to operate on itself, so we need to use a capture helper to "capture" the ? in the type of p1 into a useful type variable for what we need to do.
However, this is more complicated in this case, because you are using .getClass(). foo.getClass() has the type Class<? extends |X|> where |X| is the erasure of the compile-time type of foo. So no matter if p1 had the type Pair<A, ?> or Pair<A, T>, p1.second.getClass() would still return the type Class<? extends Marker>. So capturing on the ? in Pair<A, ?> is not enough; instead, we should capture on the ? in the return of .getClass():
#SuppressWarnings("unchecked")
private static <T extends Marker> void captureHelper(Class<T> clazz,
Pair<A, ? extends Marker> p, ClassToHandlerMap dispatch) {
Pair<A, T> p1 = (Pair<A, T>)p;
Handler<T> h1 = dispatch.get(clazz);
h1.handle(p1);
}
Unfortunately, we will have to do an unchecked cast here also. Due to the peculiar return type of .getClass() we are unable to connect the types of the return of .getClass() and the expression it is called on. And we can't use runtime casting like .cast() to cast between parameterized types (we could use .cast() to get rid of unchecked casts if we were taking an instance of the given class as an argument, but not here). There may be some edge cases in which this is incorrect, but as long as you always use Pair with the second type argument being a final implementing class, it should be correct.
And finally the primary method looks like this:
private void executeSCCE()
{
// register a handler for each Marker type
ClassToHandlerMap dispatch = new ClassToHandlerMap();
dispatch.put(B.class, new B_Handler());
dispatch.put(C.class, new C_Handler());
// get a target (e.g., Pair<A,C>)
Pair<A, ? extends Marker> p1 = getTarget();
// select handler based on the class type of the second parameter
captureHelper(p1.second.getClass(), p1, dispatch);
}
I have a variable that must meet two conditions, and I want to set them in the definition
I know that I can define either condition with an individual variable, like in any of these examples
private Class<? extends A> variable; //or
private A variable; //or
private Class<? extends B> variable; //or
private B variable;
But is there a way to have the variable meet both conditions?
I was hoping for something like this
private Class<? extends A implements B> variable;
But I can't find any way to do this without typecasting when I need to call it or storing multiple copies of it
You can declare type parameters that have multiple bounds, such as:
public static <T extends A & B> void test(Class<T> clazz)
But you cannot declare a variable that has multiple bounds:
private Class<? extends A & B> variable; // doesn't work
You can create an abstract class C that extends A and implements B, so that only one bound is required.
abstract class C extends A implements B {}
Then:
private Class<? extends C> variable;
While Java does not directly support intersection types like A&B, such types do appear in type parameter bounds and capture conversions. We can express A&B with a layer of abstraction.
public class ValueAB<T extends A&B>
{
public final T v;
// constructor ...
}
public class ClassAB<T extends A&B>
{
public final Class<T> clazz;
// constructor ...
}
Instead of A&B, Class<? extends A&B>, we use wrappers ValueAB, ClassAB
ClassAB<?> clazz = new ClassAB<>(Foo.class);
ValueAB<?> value = new ValueAB<>(clazz.c.newInstance());
value.v.methodOfA();
value.v.methodOfB();
This solution would require a wrapper for each combination of As and Bs.
Another solution is to use only A as type parameter bound; B will be supplied by wildcard bound. This is probably better if you need to express multiple A&B1, A&B2, ... types at use site.
public class ValueA<T extends A>
{
public final T v;
...
}
public class ClassA<T extends A>
{
public final Class<T> c;
...
}
---
ClassA<? extends B> clazz = new ClassA<>(Foo.class);
ValueA<? extends B> value = new ValueA<>(clazz.c.newInstance());
If it's confusing how wildcard works in these cases, see my article on wildcard
A 3rd solution is free of A or B at declaration site of wrappers; the use site provides A and B.
public class Value<T extends S, S>
{
public final T v;
...
}
public class Clazz<T extends S, S>
{
public final Class<T> c;
...
}
---
Clazz<? extends A, B> clazz = new Clazz<>(Foo.class);
Value<? extends A, B> value = new Value<>(clazz.c.newInstance());
This is however probably too confusing.
My current abstraction model does not work. I am not sure why and how should I fix it. Please refer the following classes.
public class ErrorCaptchaRequired extends AbstractError {
public String getCaptchaUrl(){
return this.captchaUrl;
}
}
public abstract class AbstractError<E extends AbstractError> {
public E getError(int errorCode){
if(error_code == 1)
return new ErrorCaptchaRequired("abc", "abc", "abc");
if(error_code == 2)
return new AnotherErrorType();
}
}
public class MyObject<E extends AbstractError>{
private E error;
public E getAbstractError(){
return error;
}
}
Later on I want to use it like this:
AbstractError<ErrorCaptchRequired> myError = myObject.getAbstractError();
String captchaUrl = myError.getCaptchaUrl();
Compilation error during return statement: Incompatible types But I am not sure why, as in my understanding since I've parametirized class I can return any type which extends from AbstractError and ErrorCaptchaRequired is extending it. Could you please suggest a fix or better design? Thanks for any help!
The problem is raw (untyped) types. Raw types have all generic info stripped from them - hence no type match.
The type of AbstractError is itself raw: Change:
public abstract class AbstractError<E extends AbstractError> {
To
public abstract class AbstractError<E extends AbstractError<E>> {
Next, ErrorCaptchaRequired extends the raw (untyped) form of AbstractError, so every instance of ErrorCaptchaRequired is then raw.
Change:
public class ErrorCaptchaRequired extends AbstractError {
To:
public class ErrorCaptchaRequired<E extends AbstractError<E>> extends AbstractError<E> {
And change:
public class MyObject<E extends AbstractError>{
To:
public class MyObject<E extends AbstractError<E>>{
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));
...;
}
}
Basically I need to have an abstract class, that contains a field of a list that details it can hold subclasses of a certain class, and then create a concrete class that stores a specific subclass of that certain class.
Better explained in code I am sure:
public class A {
}
public class B extends A {
}
public abstract class AbsClass {
protected List<? extends A> list;
}
public class ConClass extends AbsClass {
list = new ArrayList<B>();
}
With the above code i get the compiler error
The method add(capture#3-of ? extends A) in the type List < capture#3-of ? extends A> is not applicable for the arguments (B)
the line where the list is instantiated.
How can I get this to work?
Can you just type AbsClass?
public abstract class AbsClass<T extends A> {
protected List<T> list;
}
public class ConClass extends AbsClass<B> {
list = new ArrayList<B>();
}
Of course at that point it's not necessary to even move the instantiation to the subclass:
public abstract class AbsClass<T extends A> {
protected List<T> list = new ArrayList<T>();
}
public class ConClass extends AbsClass<B> {
//... other stuff ...
}