Static generic methods - java

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.

Related

Generic constructor of generic class

I have generic class ConsumerTest<T, U> and I plan that T is mutable type and U is ekvivalent immutable type like <StringBuilder, String>, <MutableInt, Integer>, <MutableDouble, Double>, ... How can I write generic constructor which creates the mutable type from immutable? Here is my attempt:
import org.apache.commons.lang3.mutable.MutableDouble;
import org.apache.commons.lang3.mutable.MutableInt;
class ConsumerTest<T, U> {
private T value;
public <T, U> ConsumerTest(U u) {
this.value = new T(u); // compile error
}
public static void main(String[] args) {
ConsumerTest ctS = new ConsumerTest<StringBuilder, String>("Hello");
ConsumerTest ctI = new ConsumerTest<MutableInt, Integer>(666);
ConsumerTest ctD = new ConsumerTest<MutableDouble, Double>(11.11);
}
}
new T(u) is invalid because T isn't a type. In order to create an instance of a class, you need to know the type, and you don't in that context.
As another answer states, new T(u) is invalid syntax. One cannot use a type parameter as a new class to instantiate, because there is no guarantee that that particular class's constructor takes exactly one argument of that exact type.
In addition, you've created a generic constructor that defines its own T and U, which shadow the class's declarations of T and U. That's why you get a compiler error message such as "Cannot convert T to T". Remove the declarations of T and U from the constructor, but not from the class.
You still need to be able to construct an object of type T from one of type U, so provide a converter function. Note here that Java's built in Functions reverse the sense of U and T that you have.
class ConsumerTest<T, U> {
private T value;
public ConsumerTest(U u, Function<U, T> converter) {
this.value = converter.apply(u);
}
public static void main(String[] args) {
ConsumerTest ctS = new ConsumerTest<StringBuilder, String>("Hello", StringBuilder::new);
ConsumerTest ctI = new ConsumerTest<MutableInt, Integer>(666, MutableInt::new);
ConsumerTest ctD = new ConsumerTest<MutableDouble, Double>(11.11, MutableDouble::new);
}
}
There may be additional reasons you need to encapsulate this functionality in a class that you haven't shown, but you may not need a class such as ConsumerTest. Just create the functions.
Function<String, StringBuilder> ctS = StringBuilder::new;
Or you may not need to bother with the functions at all. Create the mutable objects directly.
StringBuilder sb = new StringBuilder("Hello");

Function and Predicate parameter ambiguous?

Using Java 8, I get a compiler error for the following code:
public class Ambiguous {
public static void call() {
SomeDataClass data = new SomeDataClass();
callee(data, SomeDataClass::getString);
// compiler errors:
// 1. at callee method name:
// The method callee(SomeDataClass, Function<SomeDataClass,String>) is ambiguous for the type Ambiguous
// 2. at lambda:
// Type mismatch: cannot convert from boolean to String
callee(data, d -> d.getRandom() > 0.5);
}
public static void callee(SomeDataClass data, Function<SomeDataClass, String> extractString) {
System.out.println(extractString.apply(data));
}
public static void callee(SomeDataClass data, Predicate<SomeDataClass> check) {
System.out.println(check.test(data));
}
}
// token data class
final class SomeDataClass {
public String getString() {
return "string";
}
public final double getRandom() {
return Math.random();
}
}
So essentially the compiler says "I know you return boolean but you shouldn't, and if you don't I'm not sure what method to use" instead of "oh you're returning boolean, you must mean the Predicate version of the method"? How does this confusion get created?
I'd understand if Predicate<T> extends Function<T, Boolean> (so they have a common Type) but that's not the case.
I do know how to fix it; it's fine if I do
callee(data, (Predicate<SomeDataClass>) d -> d.getRandom() > 0.5);
but I'm curious what causes it.
This can be simplified a bit for clarity:
public static void m(Predicate<Integer> predicate){
}
public static void m(Function<Integer, String> function){
}
And calling it with:
m(i -> "test")
What do you think will happen? Same thing as in your question.
Compiler here has to know the method in order to find the target type, but it needs to know the target type in order to resolve the method (it's like a deadlock).
When you add a cast to Predicate..., you are creating an explicit target type, return type of which is taken into consideration for method overloading as far as I understand.

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

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

How to make a Java Generic method static?

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)

Categories