I'm trying to understand how Generics works and wrote a method to test it.
I have created Object - Drink and its child object Coffee... and defined a generic method go1() to invoke the sip() method of both these objects...
I'm running in Eclipse and get an error - stating the method sip() is undefined for type V.
Can someone explain how this is handled?
class Drink{
public void sip(){
System.out.println("Im Drink method");
}
}
class Coffee extends Drink{
public void sip(){
System.out.println("Im Coffee method");
}
}
public class test{
public static <V> V go1(V o){
o.sip();
}
public static <V> void go(V v){
System.out.println(v.hashCode());
}
public static void main(String[] args) {
Drink s1 = new Drink();
go1(s1);
int i = 10;
String j ="test";
go(i);
go(j);
}
}
Just add bounds to the type parameter:
public static <V extends Drink> void go1(V o){
o.sip();
}
With type parameter bound, you get access to the non-static methods of the bound - Drink in this case, using the reference of that type parameter.
After edit:
For passing String and Integer to your method, you can think of which common upper bound would fit for both. The common supertype for String and Integer are:
Object
Serializable
Comparable<T>
So, you can have three kinds of bounds:
<V extends Object>
<V extends Serializable>
<V extends Comparable<V>>
So you just modify your go() method like:
public static <V extends Object> void go(V v){
System.out.println(v.hashCode());
}
References:
Java Generics FAQs
What is type parameter bounds?
What is difference between <? extends Object> and <E extends Object>?
The compiler decides if method calls are valid based on the type they are called on. You have
public static <V> V go1(V o){
o.sip();
}
but V is not a bounded type so the compiler can only be sure that it is at least of type Object. Object doesn't have a sip() method. You need to bound your type
public static <V extends Drink> V go1(V o){
o.sip();
}
This way the compiler knows that V is at least of type Drink. Drink has a sip() method.
The problem is that plain <V> can't be understood as a Drink but as a generic Object. You need to bind the generic to your Drink class, so declare the generic as <V extends Drink>.
More info: Generic Methods and Bounded Type Parameters
Just to aid your understanding..
Given that your overall objective is to further your understanding of Generics, I'd just like to point out that a simple interface or superclass object reference would be a simpler solution to your issue. i.e:
public void go1(Drink d)
{
d.sip();
}
A more classic example of Generics
Let's say you've got an object, Pair. Pair needs to be able to hold two of anything in Java, so you make those two things of type Object. What does that mean? It means you're always casting between Object and the desired type. So what do you do? You use generics.
public class Pair<F,S>
{
F first;
S second;
public Pair(F first, S second)
{
this.first = first;
this.second = second;
}
}
Now instead of having to cast, you simply state:
Pair<Integer, String> pair = new Pair<Integer, String>(10, "Ten");
Related
I'm studying generics in Java, and was getting along comfortably until I reached the topic- Creating a generic method.
I know that in Java, generics are used when you want to implement something irrespective of the data type that the program(or method) operates upon. So, you could have a generic class as Class Gen<T> and then in a non-generic class class GenDemo (which includes the main()). Then, you can create Gen references for different data types, such as Gen<Integer> iOB and Gen <String> strOB.
However, in the example given on creating a generic method, the book gives the following code:
//This is a simple generic method
class GenMethDemo
{
//determine if an object is in an array
static<T,V extends T> boolean isIn(T x, V[] y)
{
for (int i=0; i<y.length; i++)
if(x.equals(y[i]))
return true;
else
return false;
}
public static void main(String[] args)
{
//use isIn() on Integers
Integer nums[]={1,2,3,4,5};
if(isIn(2,nums))
System.out.println("2 is in nums");
if(!isIn(7,nums))
System.out.println("2 is in nums");
//use isIn() on Strings
String strs[]={"one", "two", "three", "four", "five"};
if(!(isIn("two", strs))
System.out.println("two is in strs");
}
}
I understand that this program is trying to determine if a given array consists of a specified object. But I can't wrap my head around this line:
static <T,V extends T> boolean isIn(T x, V[] y)
Thinking on the lines of what I've studied so far in generics, I know that there are two arguments of the Boolean function isIn(). But what are the type-paramaters <T, V extends T> doing before the return type of the function isIn()?
I understand the use of the keyword extends here; it acts as a bound for the type-parameter V, ie: V must be of the same type as T or a subclass of T, but I can't get further.
There is a similar use of type-parameters under the topic: Creating Generic Constructors, as:
class GenCons
{
private double val;
<T extends Number> GenCons(T arg)
{
val=arg.doubleValue();
}
void showVal()
{
System.out.println("Val: "+ val);
}
}
As before, I'm stumped by the line: <T extends Number> GenCons(T arg). Why is <T extends Number> used before the constructor is declared? It could also have been written like: GenCons(<T extends Number> arg)?
Any help would be highly appreciated.
Notice how in your GenMethDemo and GenCons classes, the class itself doesn't have a generic type. It's not class GenMethDemo<T, V extends T> -- instead, it's just class GenMethDemo.
So if GenMethDemo and GenCons aren't generic classes, how are you able to use generics? It seems to be a contradiction.
Well, Java also lets you define generic methods. If I do static<T,V extends T> boolean isIn(T x, V[] y), it's as if I had actually done class GenMethDemo<T, V extends T> except that the type variables T and V are scoped only to that particular method. This can be useful when you don't necessarily want the entire class to use generics; only a method or two where they're really needed.
See below example,What are the benefits of bounded type parameters comparing f(U u) to g(I obj)?
interface I {}
class A {
public static <U extends I> void f(U u){ }
public static void g(I obj) { }
}
There aren't any, for this particular case. You can execute exactly the same operations on U (knowing that U extends I) as you can on an I.
More often you'll see cases for which it makes an actual difference:
public static <U extends I> void f(Collection<U> collection)
or, more generally, the bounded type parameter being used as a parameter for another generic type.
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)