Suppose I have and API method declared as following
public class Dummy {
public static class Result<ValueT extends Comparable> {
public ValueT value;
}
public static <ValueT extends Comparable> Result<ValueT>
getResult(Class<? extends Result<ValueT>> ofType) throws Exception {
return ofType.newInstance();
}
}
Now I'd like to invoke it relying on Java compile-time type verification and just can't find correct syntax to do this:
Attempts:
getResult(Result<Integer>.class) <-- expected syntax
public static void invokeGetResult() {
Result<Integer> intResult = getResult(Result<Integer>.class);
}
results in
error: <identifier> expected
Result<Integer> intResult = getResult(Result<Integer>.class);
^
getResult(Result.class) <-- just to try
public static void invokeGetResult() {
Result<Integer> intResult = getResult(Result.class);
}
results in
error: method getResult in class Dummy cannot be applied to given types;
Result<Integer> intResult = getResult(Result.class);
^
required: Class<? extends Result<ValueT>>
found: Class<Result>
reason: cannot infer type-variable(s) ValueT
(argument mismatch; Class<Result> cannot be converted to Class<? extends Result<ValueT>>)
where ValueT is a type-variable:
ValueT extends Comparable declared in method <ValueT>getResult(Class<? extends Result<ValueT>>)
getResult((Class<Result<Integer>>)Result.class) <-- just to try
public static void invokeGetResult() {
Result<Integer> intResult = getResult((Class<Result<Integer>>)Result.class);
}
results in
error: incompatible types: Class<Result> cannot be converted to Class<Result<Integer>>
Result<Integer> intResult = getResult((Class<Result<Integer>>)Result.class);
^
If the only purpose of passing the Class<...> is to instantiate it, consider using a Supplier<...> instead. It does not have this problem, and does not throw any exceptions.
Here is an example:
class SomeClass extends Result<Integer> {...}
public static <T extends Result<?>> T getResult(Supplier<T> constructor) {
return constructor.get();
}
Use like:
getResult(SomeClass::new); // Passes the constructor.
There isn't really such thing as a Class<Result<Integer>>. Class objects represent reifiable classes at runtime, and there is only one single Class object at runtime for the class Result, which you can get via the expression Result.class, which has type Class<Result>. There are no Class objects representing Result<Integer> or Result<String>, etc.
You can take the expression of type Class<Result> (which you get from Result.class) and do a bunch of unsafe casts on it to turn it into a Class<Result<Integer>> or something like that, for example:
`(Class<Result<Integer>>)(Class<?>)Result.class`
but not that this is unsafe. It is unsafe because the interface of Class has certain methods that return T (the type parameter of the class Class) based on runtime type operations with the class. For example, Class has a method .cast() which checks whether the passed object is an instance of the class, and if not, throws an exception, and if it is, then returns the object, as type T. If you call Result.class.cast(), it returns a Result, and the method indeed checks at runtime that the object is a Result. But if you call .cast() on an expression of type Class<Result<Integer>>, it returns type Result<Integer>, but the class could not have checked at runtime that the thing is a Result<Integer>, because 1) this is just the same Result class object, and 2) generic information doesn't exist at runtime. So you would get a Result<Integer> that might not be a Result<Integer>.
So basically, you need to think about what you are using this class object to do, and how it makes sense to have it as a Class<Result<Integer>> when all you can have at runtime is a class object representing the raw type Result.
Here you have a method that takes Class<? extends Result<ValueT>>. I am wondering why you have the wildcard here. Are you trying to have it accept subclasses of Result? Because if not, then the only thing that can be passed in is the unique Result class object itself, which means the parameter is basically pointless because it's always the same thing that can be passed in; in that case you might as well get rid of the parameter and just do:
public static <ValueT extends Comparable<? super ValueT>> Result<ValueT>
getResult() throws Exception {
return new Result<ValueT>();
}
If you are going to accept subclasses of Result, then you're assuming that all subclasses must have a no-parameter constructor, as you are calling .newInstance() on it, which is bad design. You should use something like a Supplier as Jorn Vernee's answer suggests.
EDIT: I did not understand clearly the original intentions. This example should work for your call, however it is a bit hacky and not so recommended.
public class Dummy {
public static class Result<ValueT extends Comparable> {
public ValueT value;
}
public static <ValueT extends Comparable> Result<ValueT>
getResult(Class<? extends Result<ValueT>> ofType) {
return null;
}
}
You can call it like this:
Dummy.Result<Integer> result = Dummy.getResult(new Result<Integer>(){}.getClass());
The compile time verification should work with this example, as you are creating an anonymous empty class and it's type information can be retrieved successfully.
You could also do a type cast as #Jorn suggested in comments, although your compiler will warn you about the fact it is an "unchecked cast".
Related
I'm using library which contains method I want to use. Simplified signature would look like this:
final class ThirdPartyClass
{
public static <T> void thirdPartyMethod(Class<T> clazz, T instance)
{
// does something
}
}
Inside thirdPartyMethod() it expects class of T as first argument and an instance of that specific class T as second argument.
Now I have this simple abstract class, which is calling that method:
abstract class Parent<T extends Number>
{
public Parent(T instance)
{
ThirdPartyClass.thirdPartyMethod(instance.getClass(), instance);
}
}
I'm getting an error:
method thirdPartyMethod in class ThirdPartyClass cannot be applied to given types;
required: java.lang.Class, T
found: java.lang.Class<capture#1 of ? extends java.lang.Number>, T
reason: inference variable T has incompatible bounds
equality constraints: capture#2 of ? extends java.lang.Number
lower bounds: T
How can I modify the Parent class so it conforms to expected arguments of thirdPartyMethod()?
If possible with explanation.
According to the return type of Object.getClass()
The actual result type is Class<? extends |X|> where |X| is the
erasure of the static type of the expression on which getClass is
called.
Since T extends Number, the resulting type would be Class<? extends Number>, i.e. means an unknown type extending Number. Which doesn't match T extends Number (i.e. a particular type extending Number which would be provided at runtime).
How can I modify the Parent class so it conforms to expected arguments of thirdPartyMethod()?
1. Introduce a second parameter of type Class<T> in your constructor.
abstract class Parent<T extends Number> {
public Parent(T instance, Class<T> tClass) {
ThirdPartyClass.thirdPartyMethod(tClass, instance);
}
}
2. Perform casting (as have been mentioned in the comments):
ThirdPartyClass.thirdPartyMethod((Class<T>) instance.getClass(), instance);
By the way, instead of calling methods from a constructor (especially ones that are not developed and tested by you/your colleagues) you might consider introducing a factory method that would produce your domain object based on the result of a method call.
Below is the code where I declared two methods whose return type are bounded parameters but one is based on interface whereas other is based on a class. In the test() method, assigning return type to String fails for method where bounded parameter extends from Class which is expected, but compiler doesn't throw error when the bounded parameter is based on interface. Can you explain why does this happen?
import java.io.FileInputStream;
import java.util.function.Predicate;
public class Dummy
{
<T extends Predicate<String>> T getBoundedByInterface()
{
return null;
}
<T extends FileInputStream> T getBoundedByClass() {
return null;
}
public void test()
{
String compilationError = getBoundedByClass(); //Expected
String works = getBoundedByInterface(); //No compilation error. Why?
}
}
The Java compiler will determine if the variable type can satisfy the generic return type of the method called. With a class such as FileInputStream, it knows that no class can be both a FileInputStream and a String because a class can't inherit from more than one class, so this is disallowed. Additionally we know that String is also final, but that doesn't figure into the compiler's logic.
However, with an interface, it's possible for a subclass to extend the bound and be a subclass of another class.
class Thing extends FileInputStream implements Predicate<String> {
// Implementation
}
This compiles for that reason.
Thing works = getBoundedByInterface();
Notice that it also works with String because the compiler doesn't take into account that String is final.
When a generic static method is written like this:
public static <T extends SuperClass> T foo() {
return new SupperClass();
}
, an incompatible types compilation error is thrown indicating that SuperClass cannot be converted to T.
On the other hand, when another generic static method is written like this:
public static <T extends SuperClass> void bar(T val) {
// do something....
}
the code compiles just fine when the function is called using: bar(new SuperClass()).
Also the same thing happens when using a generic class with the bounded generic type as it's type parameter,
whether it is the return type or a parameter of the function.
For example this causes the same compilation error to be thrown:
public static <T extends SupperClass> GenericClass<T> foo() {
return new GenericClass<SuperClass>();
}
indicating that GenericClass<SuperClass> cannot be converted to GenericClass<T>.However, this:
public static <T extends SuperClass> void bar(GenericClass<T> val) {
// do something....
}
compiles just fine when it is called using bar(new GenericClass<SuperClass>).
So to sum up the question, why can a generic method whose formal type is bounded with some supertype, and takes a parameter of this formal type, be called with an object of the supertype, while it can't return an object of the supertype when the return type is specified as the formal type ?
Thanks in advance.
The compiler is looking at the static type when doing compilation.
When you're trying to return a
new SupperClass()
object - the compiler must be sure that the static types are matched - and is doing so by converting from SupperClass to T.
When you'll do this casting yourself:
(T) new SupperClass()
it will give you a warning on unchecked cast - because technically you can program it but its successfulness depends on runtime types...
When you're returning void - you're returning nothing - so no casting needs to be done.
A comment on casting: casting up will always succeed (you can always loose information) casting down - depends on the dynamic type and not always succeed.
Please have a look at below code listing :-
package com.test;
class XParam implements Comparable<XParam>{
#Override
public int compareTo(XParam o) {
// TODO Auto-generated method stub
return 0;
}
}
public class Main {
public static void main(String[] args) {
Comparable<XParam> param = new XParam() ;
SomeClass object = new SomeClass() ;
object.setData( param );
}
}
Now,
object.setData( param ); //third statementt in the main method in above code listing
compiles fine, if i make SomeClass generic with formal type parameter
<T extends Comparable<T>>
as shown below :-
class SomeClass<T extends Comparable<T>>{
public void setData(T data) {
}
}
But, object.setData( param ); gives below compile time error in eclipse :-
Bound mismatch: The generic method setData(T) of type SomeClass is not applicable for the
arguments (Comparable<XParam>). The inferred type Comparable<XParam> is not a valid
substitute for the bounded parameter <T extends Comparable<T>>
if i apply the same formal type parameter at method level, rather then adding it at class level as shown below :-
class SomeClass{
public <T extends Comparable<T>>void setData(T data) {
}
}
Any good explanation/reference to understand this concept is highly appreciated.
Thanks.
I think the only reason you're getting a bound mismatch with one way and not the other is because it doesn't seem you are parametrizing the declaration of SomeClass, and are thus using a raw type. If you use a raw type, the compiler doesn't bother with any generic checks, so you get no complaints.
I think that if you attempted to genericize your class with Comparable<XParam> you would get the same error you got when you tried to call the generic method with an incorrect bound.
As for the generic method, the error makes sense.
If your bound is T extends Comparable<T>, and you pass the method a Comparable<XParam>, the compiler will infer T == Comparable<XParam> and the method will look to see if the argument extends Comparable<T> == Comparable<Comparable<XParam>>. Which it doesn't.
I believe that your method will work if you call the method with an argument of type XParam, as XParam extends Comparable<XParam>.
I have simple generic class as shown below:
public class Test<T extends Number> {
public void doSomething() {
Test t = new Test();
t.getNumber();
}
public T getNumber() {
T d = new Double("1.5"); // I get an compiler error here!
return d;
}
}
The getNumber method returns T (which extends Number) and in its implementation it instantiates a Double. The compiler is throwing up an error on the line:
T d = new Double("1.5"); // I get an compiler error here!
The error is:
Incompatible types:
[ERROR] found : java.lang.Double
[ERROR] required: T
Since T extends Number, I would have expected this to work. Am I missing something?
T extends Number, but that does not mean that it extends Double. Double is only one of the known subclasses of Number.
It's not right to use generic parameters on the left and specific implementation on the right.
You should get a compiler warning when you instantiate test.
Imagine that you instantiate Test<Integer>. Then the line in the getNumber method becomes:
Integer d = new Double("1.5");
Obviously, it should throw a compiler error.
I think that you should not use a generic type argument - replace your class with:
public class Test {
public Number getNumber() {
Number d = Double.valueOf("1.5"); // No compiler error
return d;
}
}
Another solution would be to express the condition that you can use any superclass of Double as a type argument, ie:
public class Test<T super Double> {
public T getNumber() {
T d = Double.valueOf("1.5"); // No compiler error
return d;
}
}
A few general tips:
Do not box primitive types without a good reason.
Do not use new to instantiate boxed primitive types, use the static factory methods in the boxing classes instead.
Do not use generic types without a good reason. And when you go to the hassle of defining a generic type, do not instantiate it without a type parameter.
T d = (T)new Double("1.5");
You have to type cast to T. Even though it solves your problem I feel suspicious of what you are doing.
The generic type definition class Test<T extends Number> means that whoever chooses to create an object of your Test class can choose which type parameter to use. The user chooses, not you as the class implementor.
This means you can't be sure which type it will be, and thus your getNumber() method can only safely
return an object which somehow came in as a T object
return null
return an object which some other method returning T gave back.
return an object created from a Class<T> (or Class<? extends T>)
return an object from a Collection<T> (or Collection<? extends T>)
something similar