How to make a Java Generic method static? - java

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)

Related

Static generic methods

Can you explain why the following works?
public class GenericsTest<T> {
public void doSomething(T v1, T v2) {
}
public static <T> void doSomethingStatic(T v1, T v2) {
}
public static <T> void doSomethingStaticList(List<T> v1, List<T> v2)
{
}
public static void main(String[] args) {
GenericsTest<String> gt = new GenericsTest<>();
// OK
gt.doSomething("abc", "abc");
// Not OK
gt.doSomething(1, "abc");
// OK
doSomethingStatic(1, 2);
// Still OK
doSomethingStatic(1, "abc");
// So why is this not OK?
List<String> list1=new LinkedList<>();
List<Integer> list2=new LinkedList<>();
doSomethingStaticList(list1,list2);
}
}
T v1, T v2 should be the same type in doSomethingStatic, but I'm still able to pass different types(integer and string).
If doSomethingStatic() takes a common super class by default, why doesn't doSomethingStaticList() work with different types?
In non-static case you define T as String when you create instance of GenericsTest. Hence passing an int will give compile error. If you did gt.doSomething(1, 2) it would fail as well.
In static case, you don't define T manually, it is derived from parameters. It will be the first common superclass of both classes - which in this case is Object. You might want to use bounded wildcard, e.g. <T extends Number> or <T extends CharSequence>.
Note that you have two different Ts here:
GenericsTest<T>
public static <T> void doSomethingStatic(T v1, T v2)
The declaration of generic parameter is whenever you write <T>. You can use different letters in this case to avoid confusion.
This works because T in your static method is his own type parameter, not the T parameter for instance that used in your member method. Rename it to clarify:
public static class GenericsTest<T> {
public void doSomething(T v1, T v2) {
}
public static <V> void doSomethingStatic(V v1, V v2) {
}
//...
So in case of doSomething(...) your instance type parameter value is String so it's an error. In case of static doSomethingStatic(...) value of type parameter is different:
GenericsTest.doSomethingStatic(1, "abc"); //ok
GenericsTest.<Object>doSomethingStatic(1, "abc"); //ok
GenericsTest.<String>doSomethingStatic(1, "abc"); //not ok
new GenericsTest<String>().doSomething(1, "abc"); //not ok
First a bit of theory:
Generic methods are methods that introduce their own type parameters Java Tutorials - Generic Methods
Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable Java Tutorials - Type inference
So what happen:
when a generic expression precede the return value then a new generic type variable is "declared". So the T of the class declaration is different (for the compiler) from the T of the method declaration.
the compiler apply type inference and in your example it determine that the suitable type to apply method invocation is Object
You can try also this example without static method:
public class GenericsTest<T> {
public void doSomething(T v1, T v2) {
}
public <T> void doSomething2(T v1, T v2) {
}
public static void main(String[] args) {
GenericsTest<String> gt = new GenericsTest<>();
// ok
gt.doSomething("abc", "abc");
// Not ok
gt.doSomething(1, "abc");
// ok
gt.doSomething2(1, 2);
// Still ok
gt.doSomething2(1, "abc");
}
}
In static case, you don't define T manually, it is derived from parameters.
In the case doSomethingStaticList(list1,list2) where list1 is String generic List while list2 is Integer generic List. Compiler inference algorithm won't be able to recognize because List and List doesn't belong to any common type.

Generics methods - passing objects and invoking its method

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");

Generic method without parameters

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

return type of generic methods

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

Java Class hierarchies with Generics, Comparator and sort error

I've been looking around to see if I find something to help me with my problem, but no luck until now. I've got the following classese:
public interface ISort<T> {
public List<T> sort(List<T> initialList);
}
public abstract class Sort<T> implements ISort<T> {
private Comparator<? super T> comparator;
public Sort(Comparator<? super T> comparator) {
this.comparator = comparator;
}
#Override
public List<T> sort(List<T> initialList) {
ArrayList<T> list = new ArrayList<T>(initialList);
Collections.sort(list, comparator);
return list;
}
}
public abstract class InternalTreeItem<T> {
public abstract String getValue();
}
public class D extends InternalTreeItem<Integer> {
private Integer i;
public D(Integer i) {
this.i = i;
}
#Override
public String getValue() {
return i.toString();
}
public Integer getInteger() {
return i;
}
}
public class DComparator implements Comparator<D> {
#Override
public int compare(D o1, D o2) {
return o1.getInteger() - o2.getInteger();
}
}
public class DSort extends Sort<D> {
public DSort(Comparator<D> comparator) {
super(comparator);
}
public DSort() {
super(new DComparator());
}
}
And the test class:
public class TestClass {
#Test
public void test1() {
List<InternalTreeItem<?>> list= new ArrayList<InternalTreeItem<?>>();
list.add(new D(1));
list.add(new D(10));
list.add(new D(5));
ISort<?> sorter = new DSort();
sorter.sort(list);
}
}
The compiler gives an error at the line
sorter.sort(list);
and states
The method sort(List<capture#2-of ?>)
in the type ISort<capture#2-of ?>
is not applicable for the arguments
(List<InternalTreeItem<?>>)
Ok, after a couple of hours and help from a friend, we realized the problem lies with Collections#sort(List<T> list, Comparator<? super T> c) in the abstract class Sort, as I use a Comparator<? extends T>.
I use generics, as I have 2 models, one model's super class is a generic abstract subclassed by 35 classes, and the second model actually has 2 different super classes, which combined, are subclassed by again 35 classes. These hierarchies are given, there's nothing I can do to modify them.
The model here is very simple, but you get the point. Also, there's a factory, that depending on the type of T, returns one sorter, or another.
Can any one please help and provide a solution for my issue (that is to sort a generic list; the parameter type can be a generic superclass or one of it's subclasses).
Thanks and best regards,
Domi
One way to approach this is to use a wrapper class for the classes that you cannot change.
So in your example you want to order a list of object D, based on an Integer value. By putting your objects in a wrapper and then adding this to the list, you can expose the value you wish to sort the list by.
For example, you could define an interface like:
private interface SortableListItem<T> extends Comparable<SortableListItem<T>> {
public T getValue();
}
Then, create a wrapper class for D:
public class DWrapper implements SortableListItem<Integer> {
private D item;
public DWrapper(D item) {
this.item = item;
}
public Integer getValue() {
return item.getInteger();
}
public int compareTo(SortableListItem<Integer> o) {
return getValue().compareTo(o.getValue());
}
}
From here it is pretty simple to create and sort your list:
D item1= new D(1);
D item2= new D(10);
D item3= new D(5);
DWrapper wrapper1 = new DWrapper(item1);
DWrapper wrapper2= new DWrapper(item2);
DWrapper wrapper3= new DWrapper(item3);
List<SortableListItem<Integer>> sortableList = new ArrayList<SortableListItem<Integer>>();
sortableList.add(wrapper1 );
sortableList.add(wrapper2);
sortableList.add(wrapper3);
Collections.sort(sortableList);
You can of course make the wrapper class accept a more generic object - the key is that each object returns a value (in this case an Integer) that the List can be sorted by.
The variable sorter is of type ISort<?>. It could have, say, an ISort<String> assigned to it. The sort method takes an argument of List<T> where T could be String. Clearly you cannot use List<InternalTreeItem<?>> for List<String>, so fortunately the compiler points out the error.
(Note: It's generally a good idea to keep to coding conventions. No I Hungarian prefixes, or single letter class names.)
Running your code what I can deduce is that you get a compile error since it is not possible to capture the wildcard that you specify in below line of class TestClass:
ISort<?> sorter = new DSort();
As I understand an occurrence of wild card is taken to stand for some unknown type and from your code it is not possible to infer the type (for the compiler).
But looking at the code, the class DSort is not written in a way to take type parameters
and any attempt to pass type parameters during creation of instance of DSort gave the error:
The type DSort is not generic; it cannot be parameterized with arguments
But you mention that you cannot alter the code of the modules (i.e I presume of classes DSort etc).
So one way to fix the error would be to not use generics during creation of instance of ISort.
The below code works and the prints the the sorted output (1,5,10)
List<InternalTreeItem<?>> list= new ArrayList<InternalTreeItem<?>>();
list.add(new D(1));
list.add(new D(10));
list.add(new D(5));
// no generic arguments
ISort sorter = new DSort();
List<InternalTreeItem<?>> sortedList = sorter.sort(list);
for(InternalTreeItem i:sortedList) {
System.out.println(i.getValue());
}
but results in a warning of the form ISort is a raw type. References to generic type ISort should be parameterized. But having code that uses generic and having warning of this form is not a good practice . This warning implies that the compiler cannot give cast-iron guarantee about the implicit casts it does to use generics.
If feasible, I think the better solution would be to see how the modules class can re-designed.

Categories