Java Generic Object Reuse - java

I have a class hierarchy, and would like to build a list of attribute setters for each class in the hierarchy. The code I want to write is similar to the following:
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
class Attribute<E, T> {
public final Class<T> dataType;
public final BiConsumer<E, T> setter;
public Attribute(final Class<T> dataType, final BiConsumer<E, T> setter) {
this.dataType = dataType;
this.setter = setter;
}
}
class Foo1 {
public static final List<Attribute<Foo1, ?>> foo1Attrs = new ArrayList<>();
static {
foo1Attrs.add(new Attribute<>(String.class, Foo1::setProp1));
}
public void setProp1(final String prop1) {
}
}
class Foo2 extends Foo1 {
// The following line results in an error
public static final List<Attribute<Foo2, ?>> foo2Attrs = new ArrayList<>(foo1Attrs);
static {
foo2Attrs.add(new Attribute<>(Integer.class, Foo2::setProp2));
}
public void setProp2(final Integer prop2) {
}
}
When I compile the above, I get the error:
error: incompatible types: cannot infer type arguments for ArrayList<>
public static final List<Attribute<Foo2, ?>> foo2Attrs = new ArrayList<>(foo1Attrs);
^
reason: inference variable E has incompatible bounds
equality constraints: Attribute<Foo2,?>
lower bounds: Attribute<Foo1,?>
where E is a type-variable:
E extends Object declared in class ArrayList
I understand why I am getting the above error. My question is, what is the typically pattern employed to make the above code compile and run?
The only way I can figure out how to make this work is to create a copy constructor on the Attribute class that takes an Attribute<? extends E, T>, then duplicate the data members (pointers) stored in the Attribute class into a second memory location with a different type. This seems really heavy for what should be the equivalent of a static_cast in C++. The following changes exemplify this approach:
class Attribute<E, T> {
...
public final BiConsumer<? super E, T> setter;
...
public Attribute(final Attribute<? super E, T> other) {
this.dataType = other.dataType;
this.setter = other.setter;
}
}
class Foo2 extends Foo1 {
public static final List<Attribute<Foo2, ?>> foo2Attrs = new ArrayList<>();
static {
for (Attribute<Foo1, ?> attr : foo1Attrs)
foo2Attrs.add(new Attribute<>(attr));
...
}
...
}
To reiterate the question: Is there a better approach to address this compile-time error than that outlined above?

Since foo2Attrs can contain an Attribute<Foo1, ?> where Foo1 is a supertype of Foo2, it should be declared as type List<Attribute<? super Foo2, ?>>. This makes sense because a Foo2 object can have its attributes set by any attribute setter which targets a superclass of Foo2.
Likewise, foo1Attrs should be declared as type List<Attribute<? super Foo1, ?>>.
This gets rid of the compilation error caused by the first type parameter, but there'll still be a problem when you try to write some code like foo2Attrs.get(i).setter.accept(foo, 23), because the setter is inferred as type BiConsumer<? super Foo2, ?> and the integer 23 can't be converted to the second ?.

Related

Generic bounded argument is incompatible with itself

I'm trying to create a generic method that accepts two typed arguments, one of them bounded by itself,
class Foo
{
<T extends Foo, V> void myself(final Optional<V> value, final BiConsumer<T, V> destination)
{
if (value.isPresent())
{
destination.accept(~~this~~, value.get());
}
}
}
but compiler blames on the this argument, because
error: incompatible types: Foo cannot be converted to T
destination.accept(this, value.get());
^
where T,V are type-variables:
T extends Foo declared in method <T,V>myself(Optional<V>,BiConsumer<T,V>)
V extends Object declared in method <T,V>myself(Optional<V>,BiConsumer<T,V>)
If T is a subtype of Foo, is clear that Foo is not for sure an instance of T.
But this being an extended of Foo, still is Foo.
Forcing the (T) this cast seems to ""work"".
Update
I want to use it the following way,
class Bar extends Foo
{
void setAnswer(Integer toLife)
{
}
}
----
void outThere(Bar bar)
{
bar.myself(Optional.of(42), Bar::setAnswer);
}
The proposal of wildcarded argument
class Foo
{
<V> void myself(final Optional<V> value, final BiConsumer<? super Foo, V> destination)
{
if (value.isPresent())
{
destination.accept(this, value.get());
}
}
}
fails on the usage with,
error: incompatible types: invalid method reference
bar.myself(Optional.of(42), Bar::setAnswer);
^
method setAnswer in class Bar cannot be applied to given types
required: Integer
found: Foo,V
reason: actual and formal argument lists differ in length
where V is a type-variable:
V extends Object declared in method <V>myself(Optional<V>,BiConsumer<? super Foo,V>)
T extends Foo
It's bounded by Foo, but it isn't necessarily actually Foo. It could be any subtype of Foo instead.
Instead of defining a type variable, use a wildcard:
final BiConsumer<? super Foo, V> destination
Also, a better way to write the method body is:
value.ifPresent(consumer);
(There isn't really much advantage in invoking your method over just doing this directly).
Update for your update:
If you want to express something resembling a self type, you need to add another type variable to the class:
class Foo<F extends Foo<F>>
{
<V> void myself(final Optional<V> value, final BiConsumer<? super F, V> destination) {
if (value.isPresent())
{
// (F) is an unchecked cast, but is necessary, because
// nothing constrains F to actually be "itself".
destination.accept((F) this, value.get());
}
}
Then the Bar class is defined as:
class Bar extends Foo<Bar> {
void setAnswer(Integer toLife) { /* ... */ }
}
Then the outThere method works fine:
void outThere(Bar bar)
{
bar.myself(Optional.of(42), Bar::setAnswer);
}
Ideone demo
Let's say, Foo has two subclasses, Foo1 and Foo2, both not overriding the myself() method. Then:
Foo1 me = ...;
Optional<String> value = ...
BiConsumer<Foo2,String> consumer = ...;
me.myself(value, consumer);
matches
<T extends Foo, V> void myself(final Optional<V> value, final BiConsumer<T, V> destination) {...}
with V being String and T being Foo2, while this is of class Foo1, so you can't pass it into a Foo2 consumer.
And that's what the compiler detected.
The problem is that there is no guarantee that your T is compatible with this.
It could that the BiConsumer is referring to a something that extends T, then T would not fit in. The issue is that you are inferring T and that might not be compatible with this.
If you really want this, then you should remove T all together and just use Foo.
If you want anything that extends Foo and wishes to infer that, then you could use super instead.
<V> void myself(final Optional<V> value, final BiConsumer<? super Foo, V> destination) {
if ( value.isPresent() ) {
destination.accept(this, value.get());
}
}
You are going to find some issues with this approach though.
Otherwise, you could also use Foo directly as mentioned:
public static class Foo {
<T, V> void myself(final Optional<V> value, final BiConsumer<Foo, V> destination) {
if ( value.isPresent() ) {
destination.accept(this, value.get());
}
}
}
Otherwise, if you are really sure you could cast it, but that is not really recommended.

Java generics - cast assignable capture type to subclass

I have the following scenario in Java generics:
public abstract class A<T> {
protected final Class<T> typeOfX;
public A(final Class<T> typeOfX) {
this.typeOfX = typeOfX;
}
public abstract void load(final T x);
}
public class AnyA<S> extends A<S> {
private final Map<String, A<? extends S>> map;
public AnyA(final Class<S> superTypeOfX,
final Map<String, A<? extends S>> map) {
super(superTypeOfX);
this.map = map;
}
#Override
public void load(final S superx) {
for (final A<? extends S> a: map.values())
if (a.typeOfX.isAssignableFrom(superx.getClass())) //Here I want to say: "if superx can be casted to a.typeOfX".
a.load(a.typeOfX.cast(superx)); //Here I want to cast superx to a.typeOfX (so as to call the load method). Here's the compile error.
}
}
I'm getting the error:
incompatible types: S cannot be converted to CAP#1
where S is a type-variable:
S extends Object declared in class AnyA
where CAP#1 is a fresh type-variable:
CAP#1 extends S from capture of ? extends S
AnyA is a composite A, i.e. is an A which maintains several other A instances.
AnyA in its load(...) method shall decide which of the maintained A instances should be used to "pass the loading process to" of the argument.
In other words, AnyA is responsible for finding the correct A to load the argument.
But also AnyA is an A because it handles loading the argument.
My question is:
Why is this cast not possible, by the time I know that S is a sub-class of T and all A instances in AnyA can load a subclass of S?
How can I overcome this problem without changing the class diagram too much?
I have read about "helper methods" but cannot match the example shown there to my problem.
I'm using NetBeans IDE with Java SDK 8.
Note that regardless of what you do, the code is not "syntactically type safe" in any case. There is an unchecked cast, and the only safety belt that prevents this from going wrong is the isAssignableFrom check.
(That is often OK, I'm just mentioning it for completeness)
The reason for the error may be more obvious when you pull the lines apart (here, S stands for SuperType, according to the Type Parameter Naming Conventions - please follow them!)
A<? extends S> a = ...;
S s = a.typeOfX.cast(s);
a.load(s);
The A<? extends S> intuitively means that it is an A that can accept an unknown type in its load method. You know that it extends type S, but you do not know which type this is.
It may become blatantly obvious when you insert Object for S:
A<String> specificA = ...;
// So the "specificA" can load "String" objects. Then this is fine:
A<? extends Object> a = specificA;
Object s = a.typeOfX.cast(s);
// But here's the error: "s" is only an Object, and not a String!
a.load(s);
I think the main point of confusion (and the main reason for the question) was the following: When calling
Object s = a.typeOfX.cast(s);
and typeOfX is String.class, then the return type of the cast will not be String, but only the type that the compiler can infer at this point. And this is Object, in the example above.
However, you already referred to the Helper Methods, and indeed, with some trickery, you can make this compile,
but... (see notes below)
import java.util.Map;
abstract class A<T>
{
protected final Class<T> typeOfX;
public A(Class<T> typeOfX)
{
this.typeOfX = typeOfX;
}
public abstract void load(T x);
}
class AnyA<S> extends A<S>
{
private final Map<String, A<? extends S>> map;
public AnyA(Class<S> superTypeOfX,
Map<String, A<? extends S>> map)
{
super(superTypeOfX);
this.map = map;
}
#Override
public void load(S s)
{
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(s.getClass()))
{
callLoad(a, s);
}
}
}
private static <S, T extends S> T cast(A<T> a, S s)
{
T t = a.typeOfX.cast(s);
return t;
}
private static <T, S extends T> void callLoad(A<S> a, T s)
{
a.load(cast(a, s));
}
}
I would not recommend this in practice.
Personally and subjectively: I think that when you are doing the isAssignableFrom check, then the (unchecked) cast should be as close as possible to this check. Otherwise, the code will be very hard to understand.
So although unchecked casts are a code smell in practice, and I try to avoid SuppressWarning whenever possible, I would consider this as far more readable:
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(superx.getClass()))
{
// This call is safe as of the check done above:
#SuppressWarnings("unchecked")
A<Object> castA = (A<Object>) a;
castA.load(superx);
}
}

How to infer type variables that are related from wildcard classes in Java?

I need to call generic f function from a library which takes two type parameters, as demonstrated below:
public <D extends Data<K> & Property<P>, K extends Key<D>, P extends Object>
void f(Class<D> classD, K key) {
...
}
While Property is relatively simple, Data and Key are related:
public Data<T extends Key<? extends Data<T>>> {
...
}
public Key<T extends Data<?>> {
...
}
Now my program needs to invoke the f function, however, here is what I have:
Class<?> clazz = ...;
Object key = ...;
// I know clazz and key can satisfy the constraints
// but cannot see the exact class.
f(clazz, key);
// This is what I want to invoke but this statement alone
// will lead to compile time failures.
I can create some local interfaces that satisfy the requirements and convert the two parameters to them, as demonstrated below. It can pass the compiler but will lead to cast errors.
public interface MyData<P> extends Property<P>, Data<MyKey<P>> {
}
public interface MyKey<P> extends Key<MyData<P>> {
}
So my question is whether it is possible to infer the correct types D and K from wildcard in Java (or in Groovy/Scala).
Thanks for the help!
There isn't any way to infer Class<D> from Class<?> or K from Object. The generic wildcard and Object type really mean that the compiler doesn't have any additional information about those variables' types.
Naturally, you'll get ClassCastExceptions when trying to cast an Object to a MyKey if it isn't one.
But if you know for a fact that clazz is a Class<MyFoo> and that key is a MyBar, given a MyFoo that extends Data<MyBar> and a MyBar that extends Key<MyFoo>, you could add some casts:
f((Class<MyFoo>)clazz, (MyBar)object);
The compiler will warn you about type safety, but that is the cost of passing around Class<?>s and Objects.
A Class<?> doesn't cover the bounded type <D extends Data<K> & Property<P>>, nor an Object can cover the bounded K extends Key<D>. You can specify those types in your method signature (since you can't declare them in a variable):
public class Types {
public
<D extends Data<K> & Property<P>, K extends Key<D>, P extends Object>
void f(Class<D> classD, K key) {
}
public static
<D extends Data<K> & Property<P>, K extends Key<D>, P extends Object>
void main(String[] args) {
Types t = new Types();
Class<D> klazz = null;
K key = null;
t.f(klazz, key);
}
}
interface Data<T> {}
interface Property<T> {}
interface Key<T> {}
In groovy you can go dynamic and drop the types:
class Types {
def f(Class<?> classD, key) {
"foobar"
}
static main(args) {
def t = new Types()
Class<?> klazz = null
Object key = null
assert t.f(klazz, key) == "foobar"
}
}
interface Data<T> {}
interface Property<T> {}
interface Key<T> {}

Can the return type of a static generic method depend on its arguments?

I "simply" want to write a static generic method that takes the generic Collection<E> of any type E as its input, and outputs a result of the appropriate type Vector<E>. Since the type E is always known at compile-time, this should not be a problem - but it is... Thus, a call should later look like:
Collection<String> coll = ...
Vector<String> vec = Convert.toVector(coll); // either this or...
Vector<String> vec = Convert<String>.toVector(coll);
Here is what I tried - all without success:
import java.util.Collection;
import java.util.Vector;
public class Convert<E> {
// 1st try, same type E as class => Error: Cannot make a static reference to the non-static type E
public static Vector<E> toVector1(Collection<E> coll) {
return new Vector<E>();
}
// 2nd try, a new type X. => Error: X cannot be resolved to a type
public static Vector<X> toVector2(Collection<X> coll) {
return new Vector<X>();
}
// 3rd try, using wildcard. => Error: Cannot instantiate the type Vector<?>
public static Vector<?> toVector3(Collection<?> coll) {
return new Vector<?>();
}
// 4th try, using bounded wildcard. => Error: Cannot make a static reference to the non-static type E
public static Vector<? extends E> toVector4(Collection<? extends E> coll) {
return new Vector<E>();
}
}
Is this not possible at all? And if not, is there a good reason why not? Or am I just doing it wrong? Probably there is some solution using Lambda expressions?
You should give your static method its own generic type parameter :
public static <T> Vector<T> toVector1(Collection<T> coll) {
return new Vector<T>();
}
You were missing the generic type parameter declaration (<T>) before the return type of the method.
From the JDK documentation: "For static generic methods, the type parameter section must appear before the method's return type.". So it should look like
public static <E> Vector<E> toVector1(Collection<E> coll) {
return new Vector<E>();
}
// 1st try, same type E as class => Error: Cannot make a static reference to the non-static type E
public static Vector<E> toVector1(Collection<E> coll) {
return new Vector<E>();
}
This is because you've already defined a type-parameter, called E, on instance context and the compiler doesn't allow you to use it on static context.
// 2nd try, a new type X. => Error: X cannot be resolved to a type
public static Vector<X> toVector2(Collection<X> coll) {
return new Vector<X>();
}
Here, even though you don't use the instance type-parameter E, but another one, called X, the former is not correctly defined. When introducing method-scoped type-parameters, you have to do:
public static <X> Vector<X> toVector2(Collection<X> coll) {
return new Vector<X>();
}
// 3rd try, using wildcard. => Error: Cannot instantiate the type Vector<?>
public static Vector<?> toVector3(Collection<?> coll) {
return new Vector<?>();
}
The error is simply because the wildcard <?> can be only used in return-types and on initialization, but not on instantiation (like you've done).
// 4th try, using bounded wildcard. => Error: Cannot make a static reference to the non-static type E
public static Vector<? extends E> toVector4(Collection<? extends E> coll) {
return new Vector<E>();
}
The reason is the same as 1st try. You can fix this by having:
public static <X> Vector<? extends X> toVector4(Collection<? extends X> coll) {
return new Vector<X>();
}
However, note that when you use this method, you won't be able to add anything but null to the resulting list.

Generics and Class<? extends Enum<?>>, EnumSet.allOf(class) vs class.getEnumConstants()

I have the following BeanValidation code that works fine, and permits to validate that a bean annotated with:
#EnumValue(enumClass = MyTestEnum.class)
private String field;
public enum MyTestEnum {
VAL1, VAL2;
}
Will be validated only if the field value is "VAL1" or "VAL2".
public class EnumNameValidator implements ConstraintValidator<EnumValue, String> {
private Set<String> AVAILABLE_ENUM_NAMES;
#Override
public void initialize(EnumValue enumValue) {
Class<? extends Enum<?>> enumSelected = enumValue.enumClass();
Set<? extends Enum<?>> enumInstances = Sets.newHashSet(enumSelected.getEnumConstants());
AVAILABLE_ENUM_NAMES = FluentIterable
.from(enumInstances)
.transform(PrimitiveGuavaFunctions.ENUM_TO_NAME)
.toImmutableSet();
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if ( value == null ) {
return true;
} else {
return AVAILABLE_ENUM_NAMES.contains(value);
}
}
}
What I don't understand is why my first attempt failed. Using instead of the enumSelected.getEnumConstants() above the following code:
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumSelected);
Intellij 12 doesn't highlight any error, but the compiler says:
java: method allOf in class java.util.EnumSet<E> cannot be applied to given types;
required: java.lang.Class<E>
found: java.lang.Class<capture#1 of ? extends java.lang.Enum<?>>
reason: inferred type does not conform to declared bound(s)
inferred: capture#1 of ? extends java.lang.Enum<?>
bound(s): java.lang.Enum<capture#1 of ? extends java.lang.Enum<?>>
I don't understand the problem, and I also have that code which works fine:
private static <T extends Enum<T> & EnumAlternativeName> T safeGetByAlternativeName(Class<T> enumClass, String alternativeName) {
for ( T t : EnumSet.allOf(enumClass) ) {
if ( t.getAlternativeName().equals(alternativeName) ) {
return t;
}
}
return null;
}
My guess is that in ? extends Enum<?> the two ? could be different whereas allOf expects a T extends Enum<T> where both T are the same.
For example, consider the following code:
static enum MyEnum {}
static class EnumValue<T extends Enum<T>> {
Class<T> enumClass;
EnumValue(Class<T> enumClass) {
this.enumClass = enumClass;
}
Class<T> enumClass() { return enumClass; }
}
These lines will compile:
EnumValue<?> enumValue = new EnumValue(MyEnum.class); // raw constructor
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumValue.enumClass());
because we know that the two T in enumValue.enumClass() are the same but this won't:
EnumValue enumValue = new EnumValue(MyEnum.class);
Class<? extends Enum<?>> enumSelected = enumValue.enumClass();
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumSelected);
because you have lost information by using a Class<? extends Enum<?>> as an intermediate step.
My explanation on #assylias's solution:
What we want to express about the type of the class is that it's a
Class<E>, for some E, that E <: Enum<E>
but Java does not allow us to introduce a type variable E in a method body.
Usually, we can exploit wildcard and wildcard capture to introduce a hidden type variable
class G<T extends b(T)> { ... } // b(T) is a type expression that may contain T
G<? extends A> --capture--> G<T>, for some T, that T <: A & b(T)
But this won't work in our case, since T in Class<T> does not have a bound that makes it work.
So we need to introduce a new type with the desired bound
class EnumClass<E extends Enum<E>> // called EnumValue in assylias's solution
EnumClass(Class<E> enumClass)
Class<E> enumClass()
EnumClass<?> --capture--> EnumClass<E>, for some E, that E <: Enum<E>
We then call EnumClass<E>.enumClass() to yield a
Class<E>, for some E, that E <: Enum<E>
which is the goal we've been trying to achieve.
But how can we call the constructor of EnumClass? The origin of the problem is that we don't have a proper type for enumClass, yet the constructor of EnumClass wants a properly typed enumClass.
Class<not-proper> enumClass = ...;
new EnumClass<...>(enumClass); // wont work
Fortunately(?) the raw type helps here which disables generics type checking
EnumClass raw = new EnumClass(enumClass); // no generics
EnumClass<?> wild = raw;
So the minimum gymnastics we need to perform to cast the class to the desired type is
((EnumClass<?>)new EnumClass(enumClass)).enumClass()

Categories

Resources