I was confused with my code that includes a generic method that takes no parameters, so what will be the return generic type of such a method, eg:
static <T> example<T> getObj() {
return new example<T>() {
public T getObject() {
return null;
}
};
}
and this was called via:
example<String> exm = getObj(); // it accepts anything String like in this case or Object and everything
the interface example's defination is:
public interface example<T> {
T getObject();
}
My question:example<String> exm is accepting String, Object and everything. So at what time generic return type is specified as String and how??
The compiler infers the type of T from the concrete type used on the LHS of the assignment.
From this link:
If the type parameter does not appear in the types of the method
arguments, then the compiler cannot infer the type arguments by
examining the types of the actual method arguments. If the type
parameter appears in the method's return type, then the compiler takes
a look at the context in which the return value is used. If the
method call appears as the righthand side operand of an assignment,
then the compiler tries to infer the method's type arguments from the
static type of the lefthand side operand of the assignment.
The example code in the link is similar to the one in your question:
public final class Utilities {
...
public static <T> HashSet<T> create(int size) {
return new HashSet<T>(size);
}
}
public final class Test
public static void main(String[] args) {
HashSet<Integer> hi = Utilities.create(10); // T is inferred from LHS to be `Integer`
}
}
Having such generic static declaration is possible as you can do:
example<String> x = getObj();
String s = x.getObject();//no casting required, good!
BUT getObject method becomes vague as how would you derive return type:
public T getObject() {
//how would this method return based on T?
//one way to always cast to T say:
//return (T) obj;
// but do you figure out obj based on T, NOT possible! due to type eraser at runtime
// a custom logic can produce diff type obj, but that's force casting and no generic usage
return null;
}
It's better to provide T information as argument via Class argument:
public <T> T getObject(Class<T> clazz) {
//clazz can be used to derive return value
..
}
Related
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.
I write a generic abstract class TestSuper, the remarkable is that it has a method "execute" with a param Callable, the generic type of Callable is T, and T is irrelevant to V.
this is the code:
//my abstract class
public abstract class TestSuper<V>
{
public abstract void callExecute(V value);
public <T> T execute(Callable<T> callable)
{
T call = null;
try
{
call = callable.call();
}
catch (Exception e)
{
e.printStackTrace();
}
return call;
}
}
Then I write sub class like this:
//the sub class that not specify the generic type from TestSuper
public class TestSub extends TestSuper
{
#Override
public void callExecute(Object value)
{
Boolean result = execute(new Callable<Boolean>()
{
#Override
public Boolean call() throws Exception
{
return Boolean.TRUE;
}
});
}
}
I am confused about the type of variable "result", because this code come with compile time error.
like this:
java: incompatible types
required: java.lang.Boolean
found: java.lang.Object
If I replace "Boolean result" to "Object result", the error disappear.
If I specify the generic type when difine the TestSub, there is no error too.
public class TestSub extends TestSuper<Object>
{
#Override
public void callExecute(Object value)
{
Boolean result = execute(new Callable<Boolean>()
{
#Override
public Boolean call() throws Exception
{
return Boolean.TRUE;
}
});
}
}
how to interpret the generic type in this case?
First Issue:
Since you are extending the raw type TestSuper, the execute method you get will be the one which you get after type-erasure. So, the method signature really becomes like this:
public Object execute(Callable callable)
During the type-erasure, the type parameters are replaced with their near-most bound. Since, the type parameter was <T> without any bound, near-most bound is taken as Object. That is why, you are getting that compilation error, as compiler sees execute method with return type Object, and you are storing the result in Boolean type. In fact, you should have got a compiler error at #Override annotation only, because your callExecute method is not overriding the method in super class. The return type should be Object instead of void.
Of course, when you change Boolean result to Object result, it would work.
See JLS §4.8 - Raw Types:
The type of a constructor (§8.8), instance method (§8.4, §9.4), or
non-static field (§8.3) M of a raw type C that is not inherited from
its superclasses or superinterfaces is the raw type that corresponds
to the erasure of its type in the generic declaration corresponding to
C.
So, in 2nd case, since you are extending a parameterized type TestSuper<Object>, the execute method will now be generic method only. And hence, the type T is inferred as Boolean.
If I have a method, for instance
public INode getNode(final int offset);
I assume it doesn't add something to make the method return type generic, for instance:
public <T extends INode> T getNode(final int offset);
Or did I miss something? I think generic return types are only of value if one of the parameters is of the same type (or a super/subtype)?
public <T extends INode> T getNode(final int offset);
Not only does this not provide any additional information to the caller, but is outright dangerous: The only way to implement that method signature is to use an unchecked cast, which can not be type safe, because a method's type parameters are specified by its caller (explicitly or implictly through type inference), and the type parameters aren't available to this method's implementation. For instance, consider the following program:
class NodeCollection {
private INode[] nodes = new INode[42];
public <T extends INode> T getNode(final int offset) {
return (T) nodes[offset];
}
public <T extends INode> setNode(final int offset, T node) {
nodes[offset] = node;
}
}
class ANode implements INode {}
class BNode implements INode {
void foo();
}
public class Test {
public static void main(String[] args) {
NodeCollection nc = new NodeCollection();
nc.setNode(0,new ANode());
BNode b = nc.getNode(0); // throws ClassCastException (sic!)
}
}
Best practice: Don't use an unchecked cast, unless you are really sure it'll be type correct at runtime.
I think generic return types are only of value if one of the parameters is of the same type (or a super/subtype)?
There are more cases, for instance:
public <T> T getFavorite(Class<T> clazz) {
return clazz.cast(favorites.get(clazz));
}
or
interface List<E> {
E get(int index);
}
or the examples in Colin's answer, where the type variable merely appears as type parameter in the return type, which is acceptable due to type erasure.
Edit:
I think there's no type save way if one wants to cast to the exact type of node (instead of instanceof has to precede it)
Of course there is, it's called visitor pattern.
In the example you give, it doesn't really add any value except forcing the developer of the implementation to cast the return value (in T). (Unless it has a way to get a T value, but for that it would need to call another method which returns T, so you're just moving the cast a bit further.
So not really a good idea in the case you're showing us.
There are a few cases where the generic applied only to the return value can be useful. For example:
public static <T> Collection<T> generateCollection() {
return new ArrayList<T>();
}
This allows you to create an object Collection of T without having to do any cast.
Or if you want the developer doing the implementation to cast his object (mostly works when the said object uses generics), example from Collections:
public static final <T> Set<T> emptySet() {
return (Set<T>) EMPTY_SET;
}
Resources:
angelikalanger.com: Java Generics FAQs - Generic Methods
How can I get the "real" class of a generic type?
For Example:
public class MyClass<T> {
public void method(){
//something
System.out.println(T.class) //causes a compile error, I wont the class name
//something
}
}
If T = Integer
Output:
java.lang.Integer
If T = String
Output:
java.lang.String
Thanks
If you have a instance variable of type T in your class, and it happens to be set, then you could print the class of that variable.
public class Test<T> {
T var;
public static void main(String[] args) {
Test<Integer> a = new Test<Integer>();
System.out.println(a.boo());
a.setVar(new Integer(10));
System.out.println(a.boo());
}
public String boo() {
if (var == null) {
return "Don't know yet";
}
return var.getClass().getSimpleName();
}
public void setVar(T var) {
this.var = var;
}
public T getVar() {
return var;
}
}
You can't. The information is stripped from the code at compile time, a process that is known as type erasure. For more, please look here: Type Erasure
edit: sorry my bad, the information is not loaded at run time.
As others have explained, you cannot do it in that fashion but this is how it's usually achieved in java.
public class MyClass<T> {
public void method(Class<T> clazz) {
// something
System.out.println(clazz.getName());
// something
}
}
and you use it like this
new MyClass<String>().method(String.class);
In the case of your situation, you can't. However, you might be able to use Super Type Tokens for this type of thing: http://gafter.blogspot.com/2006/12/super-type-tokens.html
An example implementation of these is the TypeReference class of the Jackson json processing library.
This is advanced stuff and probably more than you wanted to know ;-)
Note that approaches relying on 'getClass()' on an instance received with a generic type will get the actual type of that object, which is not necessarily the generic type - which would be the type by which the caller knew the instance.
For example, consider the case where the caller handles an object by an interface; when passing to generic constructs, the generic type will be the interface, not the instance's actual class.
Consider the following example "Pair" class, which allows two object references to be returned through a POJO:
public class Pair<U,V>
{
public final U first;
public final V second;
public static <U,V> Pair<U,V> of (U first, V second)
{
return new Pair<U,V> (first, second);
}
protected Pair (U first, V second)
{
this.first = first;
this.second = second;
}
}
We were considering how to modify the 'Pair.of()' factory function to return a Comparable Pair derived class, if U and V were both Comparable. However, while we can tell whether 'first' and 'second' are comparable using instanceof, we don't know that 'U' and 'V' are themselves comparable.
For this to work, the exact type of Pair returned by Pair.of() must depend on the generic types, not the actual argument types.
The following is a snippet on how to make a java generic class to append a single item to an array. How can I make appendToArray a static method. Adding static to the method signature results in compile errors.
public class ArrayUtils<E> {
public E[] appendToArray(E[] array, E item) {
E[] result = (E[])new Object[array.length+1];
result[array.length] = item;
return result;
}
}
the only thing you can do is to change your signature to
public static <E> E[] appendToArray(E[] array, E item)
Important details:
Generic expressions preceding the return value always introduce (declare) a new generic type variable.
Additionally, type variables between types (ArrayUtils) and static methods (appendToArray) never interfere with each other.
So, what does this mean:
In my answer <E> would hide the E from ArrayUtils<E> if the method wouldn't be static. AND <E> has nothing to do with the E from ArrayUtils<E>.
To reflect this fact better, a more correct answer would be:
public static <I> I[] appendToArray(I[] array, I item)
public static <E> E[] appendToArray(E[] array, E item) { ...
Note the <E>.
Static generic methods need their own generic declaration (public static <E>) separate from the class's generic declaration (public class ArrayUtils<E>).
If the compiler complains about a type ambiguity in invoking a static generic method (again not likely in your case, but, generally speaking, just in case), here's how to explicitly invoke a static generic method using a specific type (_class_.<_generictypeparams_>_methodname_):
String[] newStrings = ArrayUtils.<String>appendToArray(strings, "another string");
This would only happen if the compiler can't determine the generic type because, e.g. the generic type isn't related to the method arguments.
I'll explain it in a simple way.
Generics defined at Class level are completely separate from the generics defined at the (static) method level.
class Greet<T> {
public static <T> void sayHello(T obj) {
System.out.println("Hello " + obj);
}
}
When you see the above code anywhere, please note that the T defined at the class level has nothing to do with the T defined in the static method. The following code is also completely valid and equivalent to the above code.
class Greet<T> {
public static <E> void sayHello(E obj) {
System.out.println("Hello " + obj);
}
}
Why the static method needs to have its own generics separate from those of the Class?
This is because, the static method can be called without even
instantiating the Class. So if the Class is not yet instantiated, we
do not yet know what is T. This is the reason why the static methods
needs to have its own generics.
So, whenever you are calling the static method,
Greet.sayHello("Bob");
Greet.sayHello(123);
JVM interprets it as the following.
Greet.<String>sayHello("Bob");
Greet.<Integer>sayHello(123);
Both giving the same outputs.
Hello Bob
Hello 123
You need to move type parameter to the method level to indicate that you have a generic method rather than generic class:
public class ArrayUtils {
public static <T> E[] appendToArray(E[] array, E item) {
E[] result = (E[])new Object[array.length+1];
result[array.length] = item;
return result;
}
}
How can I make appendToArray a static method?
To make it static, you need to:
add the static modifier
add the generic type E to the method signature
Specifically for your example, that means changing the method signature from this:
public E[] appendToArray(E[] array, E item) {
to this:
// same as above, but with "static <E>" before return type E[]
public static <E> E[] appendToArray(E[] array, E item) {
The key piece that's easy to miss: the generic type should be added to the signature, appearing just before the return type. Excerpt below from
the Java Tutorial on Generic Methods:
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
The syntax for a generic method includes a list of type parameters, inside angle brackets, which appears before the method's return type. For static generic methods, the type parameter section must appear before the method's return type.
From javadoc
Generic Methods
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
The syntax for a generic method includes a list of type parameters, inside angle brackets, which appears before the method's return type. For static generic methods, the type parameter section must appear before the method's return type.
The Util class includes a generic method, compare, which compares two Pair objects:
public class Util {
public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
}
public class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public void setKey(K key) { this.key = key; }
public void setValue(V value) { this.value = value; }
public K getKey() { return key; }
public V getValue() { return value; }
}
The complete syntax for invoking this method would be:
Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);
The type has been explicitly provided, as shown in bold. Generally, this can be left out and the compiler will infer the type that is needed:
Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);
This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets.
After understanding this doc , for your question answer is that :
public static <I> I[] appendToArray(I[] array, I item)