Type inference in java - java

Could you please explain why below work in a way is does.
It seems to me the java type system is weak to infer the type of R
public class Test {
interface Parser<A,R>{
R parse(A a);
}
static class ResponseParser implements Parser<String,Integer>{
public Integer parse(String s) {
return Integer.parseInt(s) + 1;
}
}
interface Function<A,R>{
R with(A a);
}
public static <A,R,P extends Parser<A,R>> Function<P,R> getResult(final A res){
return new Function<P, R>() {
public R with(P parser) {
return parser.parse(res);
}
};
}
public static void main(String [] args){
Function<Parser<String,Integer>, Integer> func = getResult("1");
//this works
func.with(new ResponseParser());
// why this does not work
getResult("1").with(new ResponseParser());
}
}

In the getResult("1").with(new ResponseParser()); expression the type of getResult("1") sub-expression cannot be inferred correctly from context. To your opinion it should be Function<? extends Parser<String, Integer>, Integer>, but this subexpression knows nothing about Integer. In the first case you assign the result to the Function<Parser<String,Integer>, Integer>, so the R = Integer type can be resolved, but when you just call some other method, it doesn't work.
You can fix this deferring the necessity to infer the return type. Something like this:
interface ParserFunction<A> {
<R> R with(Parser<A, R> a);
}
public static <A> ParserFunction<A> getResult(final A res){
return new ParserFunction<A>() {
public <R> R with(Parser<A, R> parser) {
return parser.parse(res);
}
};
}
Now getResult("1").with(new ResponseParser()); works.

Generics are only used by the compiler to ensure that you do not violate the rules for the type you specify. During run time all generics are converted to Object but the type safety is ensured because the compiler will notify you of any violations or type safety. To achieve this though you need to tell the compiler what try you are using and this is why generics are not inferred.
Check out erasure with java generics https://docs.oracle.com/javase/tutorial/java/generics/genMethods.html

Related

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
}
}
}

Type inference issue for generic method

Is it possible to resolve the problem with weak inference without defining additional variables or type casts for code below?
public class GenericClass<T> {
public <R> R m(Class<R> cl) {
return null;
}
}
GenericClass<SomeClass> v2 = new GenericClass<SomeClass>()
.m(GenericClass.class)
.m(GenericClass.class); // <- Object cannot be converted to GenericClass<SomeClass>
Yes:
public class GenericClass<T> {
public <R> R m(Class<? super R> cl) {
return null;
}
}
GenericClass<SomeClass> v2 = new GenericClass<SomeClass>()
.<GenericClass<SomeClass>>m(GenericClass.class)
.m(GenericClass.class);
We need to fix the fact that cl might be an erased type (i.e., a super type of the generic type, R), and then we need to tell the compiler what the real type R is since the method argument is only indicating the super type.
The second call to m doesn't not need to have the generic type specified because it is inferred from the assignment.
in order to call you method m in chain it needs to return its on class "this". In your case is GenericClass. This should work.
class GenericClass<T> {
public <R> GenericClass<T> m(Class<R> cl) {
// code to do something here
return this;
}
}
for every call to the "m" method it will return it's own class then you can call it again.
I hope that helps.

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

Java: Casting to a type parameter

I have the following two classes:
public class GenericNumberOperation {
public GenericNumberOperation() {}
public <T extends Number> T getSomeValue (boolean tf) {
T number;
if(tf) {
number = new Double(1.0);
}
else {
number = new Integer(11);
}
return (T) number;
}
}
And:
public class GenericNumberTest {
public GenericNumberTest() {}
public static void main(String[] args) {
GenericNumberOperation gno = new GenericNumberOperation();
Double d = gno.getSomeValue(true);
Integer i = gno.getSomeValue(false);
}
}
When I run the test, everything is hunky-dory. If I change the type parameterization to:
public <T> T getSomeValue(boolean tf)
The compiler complains, reporting:
error: incompatible types Integer cannot be converted to T
number = new Integer(11);
where T is a type variable
T extends Object declared in method getSomeValue(boolean)
It complains similarly about the Double. Why?
EDIT:
I made a mistake. This is actually the code that works.
public class GenericNumberOperation {
public GenericNumberOperation() {}
public <T extends Number> T getSomeValue (boolean tf) {
Number number;
if(tf) {
number = new Double(1.0);
}
else {
number = new Integer(11);
}
return (T) number;
}
}
And now I understand what #Sotirios was getting at.
Forget about what you're trying to use this for. We're only going to look at this from a language perspective.
The declaration
public <T extends Number> T getSomeValue (boolean tf) {
defines a new type T that is bounded by Number. That means that a caller can only bind Number or any subtype of Number to T when invoking the method. Within the method, you don't know what that type might be.
You therefore can't do
T number = new Double(1.0);
because you don't know that T is Double. If I invoked the method as
Float f = genOp.getSomeValue(true);
T should have been Float. The compiler can't guarantee type safety and therefore rejects it (the assignment within the method, if it had been allowed, a ClassCastException would have been thrown at runtime). If you use a cast, you're telling the compiler that you're sure about what you're doing. It'll warn you, but it will trust you.
Similarly, the declaration
public <T> T getSomeValue(boolean tf)
defines a new type T that is unbounded. That means that you can bind any type to T, which makes the problem even greater. I can now do
String f = genOp.getSomeValue(true);
As #Sotirios Delimanolis wrote, you cannot even run that code.
Try this one:
#SuppressWarnings("unchecked")
public <T extends Number> T getSomeValue(boolean tf) {
T number;
if (tf) {
number = (T) new Double(1.0);
} else {
number = (T) new Integer(11);
}
return number;
}

Java generics: multiple generic parameters?

I was wondering if it's possible to write a function that accepts multiple generic types as follows:
public int void myfunction(Set<T> a, Set<T> b) {
return 5;
}
Set<Integer> setA = new HashSet<Integer>();
Set<String> setB = new HashSet<String>();
int result = myfunction(setA, setB);
Will that work? Does the generic in each parameter mean that each parameter must have the same type T that's generic?
Yes - it's possible (though not with your method signature) and yes, with your signature the types must be the same.
With the signature you have given, T must be associated to a single type (e.g. String or Integer) at the call-site. You can, however, declare method signatures which take multiple type parameters
public <S, T> void func(Set<S> s, Set<T> t)
Note in the above signature that I have declared the types S and T in the signature itself. These are therefore different to and independent of any generic types associated with the class or interface which contains the function.
public class MyClass<S, T> {
public void foo(Set<S> s, Set<T> t); //same type params as on class
public <U, V> void bar(Set<U> s, Set<V> t); //type params independent of class
}
You might like to take a look at some of the method signatures of the collection classes in the java.util package. Generics is really rather a complicated subject, especially when wildcards (? extends and ? super) are considered. For example, it's often the case that a method which might take a Set<Number> as a parameter should also accept a Set<Integer>. In which case you'd see a signature like this:
public void baz(Set<? extends T> s);
There are plenty of questions already on SO for you to look at on the subject!
Java Generics: List, List<Object>, List<?>
Java Generics (Wildcards)
What are the differences between Generics in C# and Java... and Templates in C++?
Not sure what the point of returning an int from the function is, although you could do that if you want!
Even more, you can inherit generics :)
#SuppressWarnings("unchecked")
public <T extends Something<E>, E extends Enum<E> & SomethingAware> T getSomething(Class<T> clazz) {
return (T) somethingHolderMap.get(clazz);
}
You can follow one of the below approaches:
1) Basic, single type :
//One type
public static <T> void fill(List <T> list, T val) {
for(int i=0; i<list.size(); i++){
list.set(i, val);
}
}
2) Multiple Types :
// multiple types as parameters
public static <T1, T2> String multipleTypeArgument(T1 val1, T2 val2) {
return val1+" "+val2;
}
3) Below will raise compiler error as 'T3 is not in the listing of generic types that are used in function declaration part.
//Raised compilation error
public static <T1, T2> T3 returnTypeGeneric(T1 val1, T2 val2) {
return 0;
}
Correct : Compiles fine
public static <T1, T2, T3> T3 returnTypeGeneric(T1 val1, T2 val2) {
return 0;
}
Sample Class Code :
package generics.basics;
import java.util.ArrayList;
import java.util.List;
public class GenericMethods {
/*
Declare the generic type parameter T in this method.
After the qualifiers public and static, you put <T> and
then followed it by return type, method name, and its parameters.
Observe : type of val is 'T' and not '<T>'
* */
//One type
public static <T> void fill(List <T> list, T val) {
for(int i=0; i<list.size(); i++){
list.set(i, val);
}
}
// multiple types as parameters
public static <T1, T2> String multipleTypeArgument(T1 val1, T2 val2) {
return val1+" "+val2;
}
/*// Q: To audience -> will this compile ?
*
* public static <T1, T2> T3 returnTypeGeneric(T1 val1, T2 val2) {
return 0;
}*/
public static <T1, T2, T3> T3 returnTypeGeneric(T1 val1, T2 val2) {
return null;
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
System.out.println(list.toString());
fill(list, 100);
System.out.println(list.toString());
List<String> Strlist = new ArrayList<>();
Strlist.add("Chirag");
Strlist.add("Nayak");
System.out.println(Strlist.toString());
fill(Strlist, "GOOD BOY");
System.out.println(Strlist.toString());
System.out.println(multipleTypeArgument("Chirag", 100));
System.out.println(multipleTypeArgument(100,"Nayak"));
}
}
// class definition ends
Sample Output:
[10, 20]
[100, 100]
[Chirag, Nayak]
[GOOD BOY, GOOD BOY]
Chirag 100
100 Nayak
You can declare multiple type variables on a type or method. For example, using type parameters on the method:
<P, Q> int f(Set<P>, Set<Q>) {
return 0;
}
a and b must both be sets of the same type. But nothing prevents you from writing
myfunction(Set<X> a, Set<Y> b)
In your function definition you're constraining sets a and b to the same type. You can also write
public <X,Y> void myFunction(Set<X> s1, Set<Y> s2){...}

Categories