Java generics type inference fails - java

I am trying to define a class which type could be a subtype of the given one if inferred but it doesn't seem to work with the default Java type inference mechanism and I do not understand why.
Here are some relevant pieces of code to illustrate the situation
public class ObjectChanged<T extends Something> implements TriggeringCondition<T> {
private final Class<? extends T> type;
private String featureName = null;
protected ObjectChanged(Class<? extends T> type) {
this.type = type;
}
public ObjectChanged<T> onFeature(String featureName) {
this.featureName = featureName;
return this;
}
public static <X extends Something> ObjectChanged<X> objectChanged(Class<? extends X> type) {
return new ObjectChanged<>(type);
}
}
Let's say I have one class called FastCar extending Car. I would like to build an object change for a FastCar, but to downcast it to TriggeringCondition<Car>.
If I write the following code it works as expected
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class);
}
But then if I call the onFeature(String) method it doesn't compile anymore and complains that my triggering condition if of type FastCar, which is not compatible with Car.
If now I define the objectChanged function like this
public static <X extends Something, Y extends X> ObjectChanged<X> objectChanged(Class<Y> type, Class<X> baseType) {
return new ObjectChanged<>(type);
}
Then I can use this code which resolves the problem
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class, Car.class).onFeature("something");
}
I also found out I can fix the previous build issue with this syntax, but it's quite ugly imo.
TriggeringCondition<Car> test() {
return ObjectChanged.<Car> objectChanged(FastCar.class).onFeature("test");
}
Is there a way to write the test method like this without needing an extra parameter ?
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class).onFeature("test");
}

Is there a way to write the test method like this without needing an extra parameter ?
No.
If you don't want to use the type witness (<Car>), all you can do is to assign the objectChanged result to a variable, and then call onFeature on that variable.
TriggeringCondition<Car> test() {
TriggeringCondition<Car> tc = objectChanged(FastCar.class);
return tc.onFeature("test");
}
This is a problem which crops up a lot if you use Guava's Immutable*.Builders:
ImmutableList<String> list =
ImmutableList.<String>builder()
.add("foo")
.build();
The type witness is needed here, otherwise the type of the Builder is inferred to be ImmutableList.Builder<Object>, because the type of the polyexpression is determined before the .add(String) call.
It's annoying, but that's the nature of the beast.
One thing you could do is to define a static upcast method:
static <T extends Something> ObjectChanged<T> upcast(ObjectChanged<? extends T> oc) {
ObjectChanged<T> result = new ObjectChanged<>(oc.type);
return result.onFeature("test");
}
Now you can invoke something like:
TriggeringCondition<Car> test() {
return upcast(objectChanged(FastCar.class).onFeature("test"));
}

Related

Java Method that returns different types of generic Lists

I'm currently trying to write a method that goes through a list of Ant-Objects and returns a list of AntScouts, that extend Ant. In general, List<Ant> can contain a lot of different Objects that inherit from Ant.
I also have an enum for the different kinds of ants:
public enum AntType {
QUEEN,WARRIOR,GATHERER,SCOUT;
public Class getClass(AntType type){
return switch (type) {
case QUEEN -> AntQueen.class;
case WARRIOR -> AntWarrior.class;
case GATHERER -> AntGatherer.class;
case SCOUT -> AntScout.class;
};
}
}
This enum causes a warning:
Raw use of parameterized class 'Class'
And this is the method that currently returns a List<Ant>.
public List<Ant> getAntsType(AntType type){
return ants.stream().filter(ant -> ant.getType() == type).toList();
}
How can I write the method so that it get's the AntType enum as argument and returns a List<AntScout> or List<AntWarrior> corresponding to the enum? I REALLY don't want to use Class<T> clazz as argument since that would defeat the point of the enum. (I also use that enum elsewhere, so I can't get rid of it)
How can I write the method so that it get's the AntType enum as argument and returns a List or List corresponding to the enum?
Edit: This comment probably comes closest to the desired solution:
Java Method that returns different types of generic Lists
Use the Power of Polymorphism
How can I write the method so that it get's the AntType enum as argument and returns a List or List corresponding to the enum?
You're overengineering your code for no good reason.
When you're using inheritance, your classes should be designed in a way that allow to benefit from the Polymorphism.
I.e. by using super type Ant for all your objects and interacting with them through overridden behavior without a need to discriminate between the concrete implementations and operating via type casts.
Therefore, your method returning List<Ant> is quite fine.
And even if you wanted to obtain a List<AntQueen> or List<AntScout> as a result of the method execution then you would need a to use a generic type variable T, or rather T extends Ant, and that would imply that you need a mean of representing the T. And enum would not help you with this task because in Java enums can't be generic. You need to provide as a method argument either an instance of T or a Class<T>.
public <T extends Ant> List<T> getAntsByType(Class<T> tClass) {
return ants.stream().filter(tClass::isAssignableFrom).toList();
}
But I would advise sticking with the initial version returning a List of super type Ant declaring method getType() which returns an instance of enum AntType.
public List<Ant> getAntsByType(AntType type) {
return ants.stream().filter(ant -> ant.getType() == type).toList();
}
And as I've said, Java-enums can't be generic, there's no way to obtain Class<T> through it. Hence, you can remove contrived method getClass() from AntType.
public enum AntType {
QUEEN, WARRIOR, GATHERER, SCOUT;
}
Simulated self-type
But if you're still convinced that your application logic require the ability to generate a list of concrete type like List<AntScout> from a list of super type, then you can make use of a recursive type bound.
For that, you need to define the super type as Ant<T extends Ant<T>>.
This approach is also called a simulated self-type idiom and can be observed in the declaration of the parent type of all enums java.lang.Enum<E extends Enum<E>> and in some other parts of the JDK like method Collections.sort(List<T>) where T is defined as <T extends Comparable<? super T>>.
Let's apply self-type idiom for this case.
Consider super type Ant defined as an interface, declaring a self-returning method (you can change into abstract class if you need to declare some skeletal implementations and common fields):
interface Ant<T extends Ant<T>> {
T self();
AntType getType();
}
And here's a couple of concrete classes:
public static class AntWarrior implements Ant<AntWarrior> {
#Override
public AntWarrior self() {
return this;
}
#Override
public AntType getType() {
return AntType.WARRIOR;
}
}
public static class AntScout implements Ant<AntScout> {
#Override
public AntScout self() {
return this;
}
#Override
public AntType getType() {
return AntType.SCOUT;
}
}
That how we can perform conversion using self() method:
#SuppressWarnings("unchecked")
public static <T extends Ant<T>> List<T> getAntsByType(List<Ant<?>> ants,
AntType type) {
return ants.stream()
.filter(ant -> ant.getType() == type)
.map(ant -> (T) ant.self())
.toList();
}
Usage example:
public static void main(String[] args) {
List<Ant<?>> ants = List.of(new AntWarrior(), new AntScout());
// compiles and runs without issues
List<AntWarrior> antWarriors = getAntsByType(ants, AntType.WARRIOR);
System.out.println(antWarriors);
// compiles and runs without issues
List<AntScout> antScouts = getAntsByType(ants, AntType.SCOUT);
System.out.println(antScouts);
}
Output:
[AntWarrior{}]
[AntScout{}]
A link to Online Demo
This could be possible if enums could be generic, but they can't. However, that is no big deal. Just use a final class with a bunch of public static final fields and a private constructor. A little verbose surely, but is as effective as an enum.
Also, your getClass() method should either be a static method with the switch or else be an instance method without the switch. The later is much better, so went that way. Further, calling it getClass() is not a good idea since it is unrelated with Object.getClass() method. So I called it getAntTypeClass().
And this is the result:
public class Main {
public static void main(String[] args) {
System.out.println(AntType.QUEEN.getAntTypeClass().getName());
System.out.println(AntType.SCOUT.getAntTypeClass().getName());
}
}
final class AntType<T extends Ant> {
public static final AntType<AntQueen> QUEEN = new AntType<>(AntQueen.class );
public static final AntType<AntWarrior> WARRIOR = new AntType<>(AntWarrior.class );
public static final AntType<AntGatherer> GATHERER = new AntType<>(AntGatherer.class);
public static final AntType<AntScout> SCOUT = new AntType<>(AntScout.class );
private final Class<T> antTypeClass;
private AntType(Class<T> antTypeClass) {
this.antTypeClass = antTypeClass;
}
public Class<T> getAntTypeClass() {
return antTypeClass;
}
}
interface Ant {}
class AntWarrior implements Ant {}
class AntGatherer implements Ant {}
class AntScout implements Ant {}
class AntQueen implements Ant {}
See it working on ideone.
I would change your AntType enum method so that it acts as the filter in your stream. I've had to guess at the rest of the class hierarchy but this might give you a starting point.
import java.util.List;
import static java.util.stream.Collectors.toList;
public class Demo {
private List<Ant> ants = List.of(
new AntQueen(),
new AntScout(),
new AntGatherer(),
new AntWarrior());
public static void main(String[] args) {
var demo = new Demo();
System.out.println(demo.getAntsType(AntType.QUEEN));
}
public List<Ant> getAntsType(AntType type) {
return ants.stream().filter(type::matches).collect(toList());
}
}
class Ant {}
class AntQueen extends Ant {}
class AntWarrior extends Ant {}
class AntGatherer extends Ant {}
class AntScout extends Ant {}
enum AntType {
QUEEN, WARRIOR, GATHERER, SCOUT;
public boolean matches(Ant a) {
return switch (this) {
case QUEEN -> a instanceof AntQueen;
case WARRIOR -> a instanceof AntWarrior;
case GATHERER -> a instanceof AntGatherer;
case SCOUT -> a instanceof AntScout;
};
}
}
There are a couple ways you can do this.
First, fix the method in your enum:
public enum AntType {
QUEEN,WARRIOR,GATHERER,SCOUT;
public Class<? extends Ant> getImplClass(){
return switch (this) {
case QUEEN -> AntQueen.class;
case WARRIOR -> AntWarrior.class;
case GATHERER -> AntGatherer.class;
case SCOUT -> AntScout.class;
};
}
}
Since this is a non static method, you don't need to take in the type as an argument. In your example it's not clear where the list is coming from, but if I add it as an argument, it would look like this:
public static List<Ant> getAntsType(AntType type, List<Ant> ants){
return ants.stream().filter(ant -> ant.getClass() == type.getImplClass()).toList();
}
The second way to do it would be to add a method called getType() in the Ant class which returns a type variable that is set by the constructor.
public class Ant {
private AntType type;
protected Ant(AntType type) {
this.type = type;
}
public AntType getType() {
return type;
}
}
Then you set the type in each of the subclass's constructors:
public class AntQueen extends Ant {
protected AntQueen() {
super(AntType.QUEEN);
}
}
Then the filtering code looks like this:
public static List<Ant> getAntsType(AntType type, List<Ant> ants){
return ants.stream().filter(ant -> ant.getType() == type).toList();
}

Is it possible to insert this into a generic return parameter? [duplicate]

I have an interface with a type parameter that allows its conversion into the same type with another type parameter. Like this:
interface Interfaze<A> {
public <B> Interfaze<B> convert(java.util.function.Function<A, B> f);
}
I now want to impose a stricter requirement on the return type: I want the convert method to only return the same type as it was called on. Like this:
class GoodInterfaze<A> implements Interfaze<A> {
public <B> Interfaze<B> convert(java.util.function.Function<A, B> f) {
// return new GoodInterfaze<B>(); // I want this to be allowed by compiler
// return new BadInterfaze<B>(); // I want this to be a compilation error
return null;
}
}
class BadInterfaze<A> implements Interfaze<A> {
public <B> Interfaze<B> convert(java.util.function.Function<A, B> f) {
// return new GoodInterfaze<B>(); // I want this to be a compilation error
// return new BadInterfaze<B>(); // I want this to be allowed by compiler
return null;
}
}
The Interfaze interface is under my control, so I can add extra type parameters to it (or its methods) when needed. Do Java generics allow for anything like this?
You can get close by doing this.
public interface Interfaze<T extends Interfaze<T>> {
T convert();
}
Then you can do
public class Main {
public static class Good implements Interfaze<Good> {
#Override
public Good convert() { return new Good(); } // Compiles
}
public static class Bad implements Interfaze<Bad> {
#Override
public Bad convert() { return new Good(); } // Doesn't compile
}
}
This idea of using recursive bounds like this is very common. I personally dislike it as it's very confusing and because it doesn't mix well with inheritance. For example, you can't make a subclass SubGood of Good that implements Interfaze<SubGood> because you can't implement the same generic interface with 2 different type arguments. It only really works if all implementing classes cannot be extended (that's why Enum<E extends Enum<E>> is ok).

Why does the Java compiler not let me return a generic type?

I'm a bit at a loss with generics. I have the following code:
public interface SampleValue<T> {
T getValue();
}
public static class SampleBoolean implements SampleValue<Boolean> {
#Override
public Boolean getValue() {
return Boolean.TRUE;
}
}
public static final class SampleValueGenerator {
private SampleValueGenerator() {
// do not call
}
public static <T, R extends SampleValue<T>> R forType(Class<T> clazz) {
if(Boolean.class.equals(clazz)) {
return new SampleBoolean();
}
}
}
When I try this, IntelliJ (i.e. the compiler) tells me that R and SampleBoolean are incompatible types (for the return line).
When I try the non-generic (raw) return type
public static <T> SampleValue forType(Class<T> clazz) {
I don't get any error;
public static <T, R extends SampleValue<?>> R forType(Class<T> clazz) {
(with the ? wildcard) however fails again. And for
public static <T> SampleValue<T> forType(Class<T> clazz) {
I get Incompatible types, Found: SampleBoolean, Required: SampleValue<T>.
My guess is that it has to do with e.g. List not being an ancestor of List (but a sibling), but I fail to see the wood for the trees with the above.
Can someone please explain what's going on, and why the long example doesn't work?
Update: NB: The idea was to have a few more if/else branches for different types, but I stopped when the compiler started complaining...
The reason is that your conditional doesn't prove anything to the compiler.
The confusion you're having here involves your conditional:
if(Boolean.class.equals(clazz))
With this check, you're inferring that T is a Boolean, but the compiler has no way of enforcing this. The compiler doesn't implicitly assume that this check will ensure T is Boolean. (All the compiler knows about equals in the context of this method is that it returns a boolean.)
Therefore, despite your check, R and SampleBoolean are incompatible types because R extends SampleValue<T> while T can be anything at all.
I can't really come up with a way to ensure a return of new SampleValue<T> based on T but if I do I will edit this answer with a solution. I'd love to see ideas from others about it.
I think the problem is that SampleBoolean implements SampleValue<Boolean> which is a specific type and not something generic. On the other hand, R is declared to extend a generic type SampleValue<T>.
SampleValue<T> and SampleValue<Boolean> are two different types, so this is why you get that compilation error. The forType function wants to return a generic type R and you return a specific type with the following statement:
return new SampleBoolean();
The R you are returning is always an R that implements SampleValue of Boolean and not an R that implements SampleValue of T (generic type that is set in runtime).
// if i do this
SampleValueGenerator.forType(Integer.class)
// i am expecting something that implements SampleValue<Integer>
// but you are always returning something that implements SampleValue<Boolean>
EDIT This should work (did not test yet)
public static <T, R extends SampleValue<T>> R forType(Class<T> clazz) {
return () -> {
try{
return (T)clazz.newInstance(); // Also clazz should have a default constructor.
}catch(Excepetion e){
// This catch block should be for NoSuchMethodException and InstantionException
}
}
}

Is there any hack to know the actual concrete class of the generic of an instance at runtime? [duplicate]

This question already has answers here:
Get generic type of class at runtime
(30 answers)
Closed 7 years ago.
I'd like to find a hack to infer the actual generic instance of another instance's var in runtime, without:
Changing my needed method signature (adding the helper parameter Class<T>, the obvious way)
Having to instanceof all possible subtypes in a hardcoded way
MyInterface<? extends Number> myInterface = whateverReturnsWildcardDoubleInterface();
Class<?> type = inferInstanceType(myInterface);
assert type == Double.class;
/** This is the method that represents the code I am looking for with the conrete signature**/
public <T extends Number> Class<T> inferInstanceType(MyInterface<T> myInterface){
return T.class; //Concrete T (can or cannot be the very Number)
}
Ideally, it should return Double when T is particular subtype Integer,Double.. and Number when T is Number
I checked reflection, several "TypeResolver"/"GenericResolver" libs (as the one in Spring or others in Github), but I cannot fin a way to hack it.
EDIT: I reached the conclusion that he only feasible way to do that would be some kind of very complex reflection through the stack trace up to the acutal line that passes the type in the very instantiation
EDIT2: I know it's stupid... but I solved it by simply adding a T getT() method to my interface, so I could return myInterface.getT().getClass()
Disclaimer: This solution is provided as a hack tailored to my understanding of your setup, i.e. one generic interface with a single type parameter, multiple classes, which are not themselves generic, directly implementing this one interface alone, and implementing no other generic interfaces, directly or indirectly.
Assuming that all of the above is true, there is a relatively straightforward way of hacking a solution: calling getClass().getGenericInterfaces() returns a Type object that provides the actual type with which your generic interface has been instantiated.
interface MyInterface<T extends Number> {
T getVal();
}
class DoubleImpl implements MyInterface<Double> {
public Double getVal() {return 42.42; }
}
...
public static void main (String[] args) throws java.lang.Exception {
MyInterface<? extends Number> x = new DoubleImpl();
Type[] ifs = x.getClass().getGenericInterfaces();
System.out.println(ifs.length);
for (Type c : ifs) {
System.out.println(c);
Type[] tps = ((ParameterizedType)c).getActualTypeArguments();
for (Object tp : tps) {
System.out.println("===="+tp); // <<== This produces class java.lang.Double
}
}
}
Demo.
As assylias pointed out, Java's erasure will make that information unavailable at runtime - and thus a need for a hack.
On the assumption that myInterface has a getter for T, as in, MyInterface.getValue():T (or the hack would be to add it) you could do something like this (ignoring the possibility that getValue() could return null):
public <T extends Number> Class<T> inferInstanceType(MyInterface<T> myInterface){
return myInterface.getValue().getClass()
}
Below is the full implementation
public class Q34271256 {
public static interface MyInterface<T> {
T getValue();
}
public static class MyDoubleClass implements MyInterface<Double> {
private final Double value;
public MyDoubleClass(Double value) {
this.value = value;
}
#Override
public Double getValue() {
return value;
}
}
public static class MyIntegerClass implements MyInterface<Integer> {
private final Integer value;
public MyIntegerClass(Integer value) {
this.value = value;
}
#Override
public Integer getValue() {
return value;
}
}
#SuppressWarnings("unchecked")
public static <T extends Number> Class<T> inferInstanceType(MyInterface<T> myInterface){
Number value = myInterface.getValue();
if (value == null) return null;
return (Class<T>)value.getClass();
}
public static void main(String...args) {
List<MyInterface<? extends Number>> list = Arrays.asList(
new MyDoubleClass(1.1),
new MyIntegerClass(5)
);
for (MyInterface<? extends Number> myInterface : list) {
Class<?> type = inferInstanceType(myInterface);
System.out.printf("%s inferred type is %s\n",
myInterface.getClass().getName(),
type.getName());
}
}
}
And the output should look something like this:
MyDoubleClass inferred type is java.lang.Double
MyIntegerClass inferred type is java.lang.Integer

Cast to super class type a "Class"

I have an interface
interface Inter extends Blah {
public void someMethod();
}
class Dummy {
Class<Blah> interfaceType;
public setInterfaceType( Class<Blah> input ) {
this.interfaceType = input;
}
}
class tester {
public void init() {
Dummy dummyObj = new DummyObj();
dummyObj.setInterfaceType( Inter.class ); //This complains that the type is not suitable
}
}
Compilation error:
The method setInterfaceType(Class) in the type Dummy is not applicable for the arguments (Class)
I tried casting input to Class<Blah> while calling setter but that isnt allowed either. Im not understanding why it doesnt accept a class of sub-class-type. Can anyone tell me whats happening here and how the setter can be invoked. The Dummy class is external so i cannot change it.
Generics are not covariant so you can't set to Class<Blah> object of type Class<Inter>. Think about it. If you would be able to use List<Fruit> list = new ArrayList<Apple>() then via list you would be able to add not only Apples but also other Fruits. Would that be OK?
To solve this problem try changing Class<Blah> to Class<? extends Blah>
You can also change your Dummy class to use generic type T
class class Dummy<T extends Blah> {
Class<T> interfaceType;
public void setInterfaceType(Class<T> input) {
this.interfaceType = input;
}
}
and use it like
Dummy<Inter> dummyObj = new Dummy();
dummyObj.setInterfaceType(Inter.class);
You most certainly means
Class<? extends Blah> interfaceType;
public setInterfaceType( Class<? extends Blah> input ) {
this.interfaceType = input;
}
Update
If the Dummy class can't be modified, then it is pretty dubius WHY there is such a setter method. But anyway, you may avoid the "generic type" check, by casting your argument to Class (without type argument), as in: dummyObj.setInterfaceType( (Class) Inter.class )

Categories