I came across the following snippet of code that uses generics.
public class Generics<T> {
public static <T> T replaceIfNull(T objectToCheck, T defaultValue) {
return objectToCheck == null ? defaultValue : objectToCheck;
}
public static <T> boolean CheckIfNull(T objectToCheck) {
return objectToCheck == null ? true : false;
}
}
I am having a difficult time truly understanding how generics work, formed and used. I have a high level understanding, meaning that I know the definition of generics. And by the definition my interpretation of this code snippet is that replaceIfNull method checks for null values of any object and then returns a default value (whatever that is). And that CheckIfNull method is similar, in that it checks null value for any object.
But how does this work? Why is the method formed with <T>, which seems to be a type and then there is T following. I do not understand this syntax, <T> T means? And how does T become a type in the parameters? How come this method, for example, could not be written as
public static Object replaceIfNull(Object objectToCheck, Object defaultValue) {
return objectToCheck == null ? defaultValue : objectToCheck;
}
Thank you in advance for your clarification.
Let's start with answering your last question. The method rewrite with Object instead of generics has two drawbacks. First, it will not work with primitives (which might not be a real drawback in this case, since you are checking against null, and primitives cannot take null values, but still...). Second, it will require casting. If , for example, you use this method on two strings, like replaceIfNull(myString, "Default Value"), then you would expect to get a String as an output, right? But instead the method declared to be returning Object; so there is no way for compiler to know that it will return a String, and you will have to do casting every time you use it: `String result = (String) replaceIfNull(myString, "Default Value");' Generics were introduced specifically to fix this situation.
You can think of generics as templates; whatever type you put in the angle braces will be used later in the code whenever you use type parameter. So, <T> means: "Here will go some type; replace T with it everywhere in the code".
And the last question is about the method signature. I think here you mixed up two different methods of using generics - on a class level, and on a method level. Since you've already introduced the type parameter on the class level, there is no need to do it again on a method level, so I think you can safely remove <T> from method declaration.
I suggest you read a more detailed explanation on generics here: http://docs.oracle.com/javase/tutorial/java/generics/
The subject of Generics is very broad and you can read about it extensively on Oracle's website or on Stack Overflow.
Why is the method formed with <T>, which seems to be a type and then there is T following.
This is a generic method. The <T> declares a new type variable with no bounds.
// v return type
public static <T> T replaceIfNull(T objectToCheck, T defaultValue) {
// ^ new type variable ^ type variable used as a type
Within the method body, since the type variable has no bounds, T can, at most, be interpreted as Object. You're only going to have access to method declared in Object on expressions of type T.
Outside the method, ie. in invocation contexts, the type variable T will receive a concrete type value. That is, it will either infer it from its invocation context or it will be provided explicitly.
For example, in
replaceIfNull(someStringVar, otherStringVar);
the type variable T will be bound to String, so all usages of T will be interpreted as String. You could therefore do
String notNull = replaceIfNull(someStringVar, " not null ");
You could also provide the type argument explicitly
Generics.<String>replaceIfNull(nullVar, " not null ");
and now again the type variable will be bound to String.
Note that the type variable T declared at the type level
public class Generics<T>
is completely different from the type variable T declared in the method.
Related
I have learned that in compile time generics we use changes to most specific type. so what is the benefit of generics?
for example in this code:
int a=1;
double b = 2.1;
printArray(a,b);
public static <E> void printArray(E first , E second);
in compile time E changes to Number (because the specific parent of int and double is Number)
so what is the benefit of generics when we can write Number instead of E in here?
The purpose of generics is not related to the fact that the generic parameter will be inferred to be the most specific type. The purpose of generic methods is to allow you to write a method that can work with any type and at the same time, maintain type safety.
Your printArray method can not only take Number as arguments, but also String, SomeClass, Object and all sorts of reference types.
At this point you might ask "can't we just write Object as the parameter types?" Sure with the type being Object, the parameter will not only accept all reference types, but the primitives will also be accepted and boxed! So in this specific case, you don't necessarily have to use a generic method.
However, there are times when generic methods shine.
One of these occasions is when the methods work with generic types. For example, this is Optional.ofNullable:
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
It creates a Nullable<T> depending on whether the parameter is null. If this were not generic, it would have to return Optional<Object>. Every time you want to call get or orElse, you need to cast the result to the type you want. If you made a mistake in one of the casts, a ClassCastException might be thrown at runtime. But with generics, the compiler checks the types for you!
Here's another example:
public static <T> T first(List<T> list) { return list.get(0); }
Without generics, you would have to use the raw type List and return an Object. This again creates the problems of casting and reduced type safety.
Below are the two lines of my code snippet:
List<String> listDevs = Arrays.asList("alvin", "Alchemist", "brutus", "larsen", "jason", "Kevin");
listDevs.sort(Comparator.comparing(String::length)); //This works fine
listDevs.sort(String::compareToIgnoreCase); //This works fine
But (out of expermient) when I try to write
listDevs.sort(Comparator.comparing(String::compareToIgnoreCase));
The compiler throws error
Cannot make a static reference to the non-static method
compareToIgnoreCase(String) from the type String
Similar happens to the below code
listDevs.sort(Comparator.comparing(String::compareTo));
I understand the error and that it works fine if I remove the Comparator.comparing (as shown above).
But my point is, how does this line works?
listDevs.sort(Comparator.comparing(String::length));
I believe I am missing something. I have read this thread. Is this the same scenario?
Comparator.comparing expects a Function which describes a comparable property of the elements. So String::length is sufficient as length() is a property of the String evaluating a String to an int (that’s why comparingInt is preferable here).
In contrast, String.compareToIgnoreCase and String.compareTo are comparison methods. They compare two String objects. So references to them are sufficient where a Comparator is expected, but not where a property Function is expected.
It’s like you have a factory saying “Gimme an engine, and we build a car for you” and you are trying to give them a complete car. While that existing car is valid where a car is expected, there is no sense in passing it to the factory to built a car.
Unfortunately, the current compiler implementation is very bad at reporting error with functional signatures. You will almost always see messages like “Cannot make a static reference to the non-static method …” when signatures mismatch.
The sort method expected a Comparator.
When you do this, you are indeed providing one.
listDevs.sort(Comparator.comparing(String::length));
Same happens here(but a bit non-intuitive):
listDevs.sort(String::compareToIgnoreCase)
listDevs.sort((left, right) -> left.compareToIgnoreCase(right)); // same thing as above
That's exactly the definition of a Comparator - take two Strings and return an int.
The line that you say how come this works: listDevs.sort(Comparator.comparing(String::length)); is actually pretty simple.
Comparator.comparing takes a Function that transforms your input type into something that is Comparable. In your case takes a String and returns an Integer; which is Comparable.
JLS says Compile-Time Declaration of a Method Reference of ReferenceType :: [TypeArguments] Identifier can be interpreted in different ways.
Given a targeted function type with n parameters, a set of potentially applicable methods is identified:
ReferenceType :: [TypeArguments] Identifier has two different arities, n and n-1, are considered, to account for the possibility that this form refers to either a static method or an instance method.
A method reference expression of the form ReferenceType :: [TypeArguments] Identifier can be interpreted in different ways. If Identifier refers to an instance method, then the implicit lambda expression has an extra parameter with type of this compared to if Identifier refers to a static method. It is possible for ReferenceType to have both kinds of applicable methods, so the search algorithm described above identifies them separately, since there are different parameter types for each case.
Comparator.comparing method accept a Function<T,R extends Comparable<? super R>>. when you use String::compareToIgnoreCase that will reports error,because it has two parameters one is implicit this another is a comparing string of method parameter, so it is more like a BiFunction<String,String,Integer> not a Function<String,Integer>.
BiFunction<String, String, Integer> comparator = String::compareToIgnoreCase;
// you can't assign a BiFunction to a Function
// because one is incompatiable with another.
Function<String,Integer> function = comparator;
Stream.sort method accept a Comparator, and Comparator is more like a BiFunction<T,T,Integer> so it is compatiable with String::compareToIgnoreCase. on the other hand, they can be interchangeable. for example:
Comparator<String> primary = String::compareToIgnoreCase;
BiFunction<String, String, Integer> comparator1 = primary::compare;
Comparator<String> comparator2 = comparator1::apply;
you can using comparing(String::toLowerCase) instead, it is equalivent to String::compareToIgnoreCase, for example:
// String::compareToIgnoreCase
listDevs.sort(String::compareToIgnoreCase);
// comparing(String::toLowerCase)
listDevs.sort(comparing(String::toLowerCase))
I started reading some article about generics in Java and one thing confused me:
public static <t> T getFirst(List<T> list)
"This method will accept a reference to a List and will return an object of type T."
cool, but what does <t> do (the lower case one, after static)? I tried and failed to understand it...
It should rather be :
public static <T> T getFirst(List<T> list)
So, what does the <T> mean ?
It means that there is a type called <T> for the scope of this method. And the method will return an instance of that type T, also.
How to use it ?
If you pass a List<String> the method should return the first String in the list.
If you pass a List<Integer> the method should return the first Integer in the list.
What's the point of supporting such methods ?
You don't have to overload methods with different signatures, according to the type of the objects in the List and
You don't have to worry that the method will return other type than the type of the objects in the list, which will relief you from the burden of possible casting, for example.
Maybe it is more understandable like this:
public static <T> T getFirst(T something)
In short means that the type T will be a parameter given later when you call the method:
String text = "text";
getfirst(text);
Now the compiler will know that all the T means String here.
In C#, the default keyword sets a default value of a type parameter. This will be null for reference types and zero for value types.
I'm looking for an equivalent keyword or construct in Java. I'm very fuzzy on reference vs. value types in Java - do they behave the same?
I've got a function defined as
private T BuildEntityFromResultSet(ResultSet resultSet)
{
// Initialize T appropriately
T entity = <blank>; // default(T) in C#.
}
Is there a keyword to initialize T properly that can fill in the blank above? Is it necessary to be concerned about an initial value (other than null) in Java?
I saw this question, but it doesn't seem to answer mine.
This functionality is not needed in Java, since type parameters are erased at runtime, i.e. your field entity will be of type Object after compilation. Therefore you cannot have primitive types (what you call value types) as type parameters in Java at all. And the default value for all reference types is null.
There is no corresponding construct in Java. Primitive numbers are always defaulted to zero; boolean to false, and Objects to null. But Java have no value types.
With generics, this functionality can be avoided. Consider:
public Holder<T> {
private T _value;
public reset() {
_value = ??
}
}
Here, one cannot use Holder<int>, only Holder<Integer>, so _value can always be reset null.
I've found a place where this functionality is missed, however. If you are writing an InvocationHandler for a Java Proxy, and the method's return type is a primitive, simply returning null does not work.
I would like to be able to detirmine the return type of my method call at runtime, but I can not seem to be able to get the Type of T.
public <T> T getT()
{
Object t = null;
Class<?> c = t.getClass();
System.out.println(c.getName());
return (T) t;
}
Is there any way to determine the Type of T at runtime in Java?
Your function will throw a NullPointerException, because you call "getClass" on a null pointer (since t is initialized with null). Additionally, generics are used for giving added compile-time type-checking. They do not give you anything special at runtime; generics simply use type Object, but cause the code which uses the generic object to perform implicit casts and also causes the compiler to be aware of how you will use it.
Java generics are a static type checking feature. Attempting to retrieve reflection artifacts from generic parameters is typical of poorly thought out design.
In the question example, there is no guarantee that T is a class or even interface. For example
List<? extends Frogs> list = thing.getT();
If you really want to go down this path (and I strongly suggest you don't, not that I expect you to take any notice), then you can supply a reflection object that is statically related to the generic parameter as an argument:
public <T> T getT(Class<T> clazz) {
Object value = map.get(clazz);
return clazz.cast(value);
}
If you have a generic Class you can write a constructor that takes the type and saves it into a member of your class. This way you can check the Type during runtime. All information that are only in the generics are gone after compiling.