Java: Casting to a type parameter - java

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

Related

How these strange override rules with erasure can be explained?

What is happening here and how to understand it?
class C<T> {
public T id(T x) {
return null;
}
}
class D extends C<String> {
public Integer id(Integer x) { // compiles! but T shall be String ???
return 7;
}
}
public static void main(String[] args) {
System.out.println(new D().id("7")); // null
System.out.println(new D().id(7)); // 7
}
By the way if I declare D like this, compilation fails with Name clash: The method id(Object) of type D has the same erasure as id(T) of type C<T> but does not override it:
class D extends C<String> {
public Object id(Object x) { // compile error !
return 7;
}
}
The compiler replaces your type parameters with Object after it's checked that they've been used correctly. So after compilation, your original class D has
public Object id(Object x)
and
public Integer id(Integer x)
which is perfectly fine.
However, in your second example, there are two versions of public Object id(Object x) in the same class.
Use #Override annotation to check if the method really overrides another method.
In the first case you will have 2 methods, one that expect String and another that expect Integer.
In the second case you created the same method that already exits after type erasure of supertype.

Java Generics with Static Factory methods

I'm trying to create a Variable class that can represent either an Integer or Double value using generics.
Below is the code that I have tried. Because of erasure I use an enum to store the intended type of the Variable and then try and use this to initialise the value to the correct type.
public class Variable<T> {
private enum Type {INTEGER, DOUBLE};
private Type type;
private T value;
public static Variable<Integer> createAsInteger() {
return new Variable<Integer>(Type.INTEGER);
}
public static Variable<Double> createAsDouble() {
return new Variable<Double>(Type.DOUBLE);
}
private Variable(Type type) {
this.type = type;
if(type == Type.INTEGER) {
value = new Integer(0);
} else {
value = new Double(0.0);
}
}
public static void main(String[] args) {
Variable.createAsInteger();
Variable.createAsDouble();
}
}
However when I compile it I get the following message...
error: incompatible types: Integer cannot be converted to T
value = new Integer(0);
and likewise for the Double.
Can anyone explain why this is happening and if there is a way round this without having to write two separate classes, one for Integer and one for Double?
Edit
Thanks for all your answers...based on them I now realise there are better ways of doing this. However I'm trying to get my head round why this approach isn't working so that I don't make the same mistake in the future.
When I define my class as public class Variable<T extends Number> as suggested, I still get the same error.
Your architecture seems to defile the concept of generics.
The simplest way would be to have an upper bound in your type parameter:
class Variable<T extends Number> {...}
Then you can have a generic factory method creating a Variable<X> based on your required class:
static <X extends Number>Variable<X> create() {
return new Variable<X>();
}
You can then invoke it as:
Variable.<Integer>create(); // returns an instance of `Variable<Integer>`
This will not limit to Integer and Double, but rather any Number.
If you have to, you can limit those choices by performing the following:
Add a parameter to your create method: create(Class<X> clazz)
Check the value of your clazz argument within the method's body:
if (!clazz.equals(Integer.class) && !clazz.equals(Double.class)) {
// TODO complain
}
Otherwise, you can ensure you use a private constructor and provide static createAs... non-generic methods such as createAsInteger etc., that would return a new Variable<Integer> etc.
The problem here is that T can be anything. What if T was for instance String, your code would amount to:
String value = new Integer(0);
You could lay out your factory methods like this:
public static Variable<Integer> createAsInteger() {
return new Variable<>(new Integer(0), Type.INTEGER);
}
Where you have a constructor like:
private Variable(T value, Type type) {
this.value = value;
this.type = type;
}
You get the error because you are typizing a method inside a generic class. You can't define some inside the T generic class.
By the way you are mistaking the design pattern.
You have to design a generic class for Variable, also the constructor must have T as argument type.
In an other class you implement the factory with the createInteger and the createDouble methods.
You can make your class inherit from Numbers and use type checking to invoke appropriate method for Integer or Double.
public class Variable<T extends Number> {
public static Variable<T extends Number> Variable<T> create(Variable<T> var){
if (var instanceOf Integer){
// Take appropriate action
}
else if (var instanceOf Double){
// Take appropriate action
}
}
}
By this, there is no peculiar need of maintaining a separate enum for Types.

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

Type inference in 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

Generic type extending Number, calculations

I've made an interface of math operation with one method, calculate, taking various number of arguments
public interface MathOperation {
public <T extends Number> T calculate(T... args);
}
There's also simple implementation of this class, which does not work:
private class Sum implements MathOperation {
#Override
public <T extends Number> T calculate(T... args) {
return args[0] + args[1];
}
}
The problem is:
bad operand types for binary operator '+'
first type: T
second type: T
where T is a type-variable:
T extends Number declared in method <T>calculate(T...)
What I'm trying to achieve is a simple class, taking for example two Doubles and returning Double as well.
Is there possibility to achieve this?
+ cannot be applied to types that extend Number. new Integer(5) + new Integer(5) works because of autoboxing. You will have to look at the runtime type of args and do the operation accordingly.
Something on the lines of:
private class Sum implements MathOperation {
#Override
public <T extends Number> T calculate(Class<T> clazz, T... args) {
if (clazz.equals(Integer.class))
{
return Integer.class.cast(args[0]) + Integer.class.cast(args[1]);
} else (....)
}
}
For Addition we can use doubleValue() method of Number class. To return the same type value, the idea is to use a Function or Supplier or a Factory to create instances of the type T.
class MathOperation<T extends Number> {
public double add(T a, T b) {
double d = a.doubleValue() + b.doubleValue();
return d;
}
public T add(T a, T b, Function<Double,T> function) {
double d = a.doubleValue() + b.doubleValue();
return function.apply(d);
}
}
You can test the runtime type as shown in the other answers. Or you can try a different design: Create an abstract class that works as a factory:
interface MathContext<T extends Number> {
...
T valueOf(double d);
T valueOf(int i);
T add (T... args);
}
And concrete classes for the types that you want to use:
DoubleContext implements MathContext<Double> {
...
Double valueOf(int i) {
return i;
}
Double valueOf(double d) {
return d;
}
Double add(Double... args) {
Double res = 0;
for (Double arg: args) {
res += arg;
}
return res;
}
}
Now you could implement your MathOperation using that class. However, it's not really needed any more.

Categories