What is the purpose of #CompatibleWith annotation from Guava? - java

From the documentation of com.google.errorprone.annotations.CompatibleWith:
Declares that a parameter to a method must be "compatible with" one of the type parameters in the
method's enclosing class, or on the method itself. "Compatible with" means that there can exist a
"reference casting conversion" from one type to the other (JLS 5.5.1).
For example, Collection.contains(java.lang.Object) would be annotated as follows:
interface Collection<E> {
boolean contains(#CompatibleWith("E") Object o);
}
To indicate that invocations of Collection.contains(java.lang.Object) must be passed an argument whose type is compatible with the generic type argument of the Collection instance:
Here is a usage from com.google.common.cache.Cache:
public interface Cache<K, V> {
V getIfPresent(#CompatibleWith("K") Object key);
V get(K key, Callable<? extends V> loader) throws ExecutionException;
...
What is the benefit of having #CompatibleWith("E") Object instead of E as the type of the parameter? And why did they use the #CompatibleWith annotation in the getIfPresent but not in the get method from Cache?

It's safe for getIfPresent operation to allow objects of "too broad" type (you don't get anything from cache with string keys from getIfPresent(42)). On the other hand, for hypothetical get(Object, Callable) allowing inserting an object of wrong type (eg. 42 instead of a string "foo") would damage the underlying collection, that's why you have compile time checking won't allow it.
Having said that, this code:
Cache<String, Foo> cache = CacheBuilder.newBuilder()
// and later
Foo value = cache.getIfPresent(42);
is most probably wrong, and it makes sense for framework like Error Prone to signal that as a possible bug.
More detailed clarification about "use Object not generic type in safe operations" convention (which isn't used only in Guava, but also in JDK collections framework) is explained in this old, but still relevant blog post "Why does Set.contains() take an Object, not an E?", where you read:
Why should code like the following compile?
Set<Long> set = new HashSet<Long>();
set.add(10L);
if (set.contains(10)) {
// we won't get here!
}
We're asking if the set contains the Integer ten; it's an "obvious"
bug, but the compiler won't catch it because Set.contains() accepts
Object. Isn't this stupid and evil?
and later answers the question in title:
The real difference is that add() can cause "damage" to the collection when called with the wrong type, and contains() and remove() cannot.
The conclusion is also relevant:
Static analysis plays an extremely important role in the construction of bug-free software.
Which makes sense, because the author, Kevin Bourrillion, is also lead developer of Guava.

Related

How to specify the generic type of a collection?

I want to define a function which can convert a kind of Set to another, like convert HashSet to LinkedHashSet. Here is the function declaration. The sp is sharedpreferences.
public Set<String> decodeStringSet(String key, #Nullable Set<String> defaultValue, Class<? extends Set> cls){
Set<String> result = sp.getStringSet(key, defaultValue);
if(result == null){
return defaultValue;
}
else {
String[] array = result.toArray(new String[0]);
Set<String> a;
try {
a = cls.newInstance();
} catch (IllegalAccessException | InstantiationException var7) {
return defaultValue;
}
a.addAll(Arrays.asList(array));
return a;
}
}
However, the compiler remind me that "Unchecked assignment: '? extends java.util.Set' to 'java.util.Set<java.lang.String>'" on "a = cls.newInstance();". I don't know how to change cls to cls<java.lang.String>.
The warning is unavoidable. Isolate it in a helper method and toss the appropriate #SuppressWarnings at it. Or, refactor how this thing works. In general, the generics of Class<?> are weird and don't work well; if you try to write code that relies on the generics part to make it work, it's likely to result in many situations where you can't avoid these warnings, and the API is suboptimal.1
One tricky way to do what you're trying to do here in a one-size-fits-all way is so-called Super Type Tokens. You can search the web for this concept, because for what you're specifically doing here, STTs are overkill. What you are looking for, is a supplier.
You want the caller not to pass you the type of a set. No. You want the caller to pass you a piece of code that, if executed, creates the set.
While we're at it, let's get rid of the array, you're shifting the elements through that array for absolutely no sensible reason.
public <S extends Set<String>> S decodeStringSet(String key, #Nullable Set<String> defaultValue, Supplier<S> setMaker) {
Set<String> result = sp.getStringSet(key, defaultValue);
if(result == null) return defaultValue;
S a = setMaker.get();
a.addAll(result);
return a;
}
This code can be used as follows:
LinkedHashSet<String> mySet = decodeStringSet("myKey", null, LinkedHashSet::new);
Perhaps you're unfamiliar with this syntax. new LinkedHashSet() will, when you run that code, create a LinkedHashSet. In contrast, LinkedHashSet::new will, when you run that code, produce an object that can be asked to create a LinkedHashSet, by invoking its get() method. One does the act right this very moment. The other wraps 'do the act' into a little machine. You can hand the machine to other code, or press the button on the machine to make it do the act, and you can press the button as often as you feel like.
[1] Need some more explanations as to why relying on the generics of j.l.Class is awkward and not a good idea?
A class object simply cannot, itself, represent generics, whereas generics can represent generics. That is: List<List<String>> is perfectly fine. However, Class<List<String>> does not make sense. You can write it, (j.l.Class does not have hardcoded rules to keep sanity alive in the langspec), but it doesn't represent anything: There's just one class object that represents the type j.u.List. This one object cannot therefore represent the generics; you can't have one class object representing List<String> and another representing List<Integer>. Less important, but still annoying - there are things class objects can represent that generics cannot. int.class is types as Class<Integer> but this isn't quite right.
Hence, in your example, the compiler consider Class<? extends Set> as problematic; it's got a raw type inside the generics. However, it is technically correct, in that it is not possible to represent e.g. a Set<T>, merely 'a Set, whose generics are unknown, given that j.l.Class objects cannot represent them'.
Lastly, classes basically only produce (the P in PECS - which explains what the difference is between <Number>, <? extends Number>, and <? super Number>); it is mentally difficult to fathom the difference between Class<? extends String> and Class<String>, because it's an irrelevant difference, given that j.l.Class only produces. And yet, often you really do need to write Class<? extends String> because if you don't, the compiler refuses to compile your code for imaginary, irrelevant reasons. That's because, again, j.l.Class is not hardcoded in the lang spec: The compiler does not know that there is no effective distinction between Class<T> and Class<? extends T>, and java does not have a way to mark off a given generics param as forced Produces-only or some such.

What is the benefit of using Class<?> instead of Class as a method parameter type?

NB: This is not a duplicate of the question I have already linked to below. I obviously read that question/answer first before posting and did not have my question answered in any form.
This linked question does go into more detail explaining why the generic Class exists. However I don't get an answer specifically to the benefits of Class in my situation.
What does the generic nature of the class Class<T> mean? What is T?
I've written a utility method that accepts a parameter 'cl' of type Class and performs logic by using cl.isInstance(objectInstance) method.
However I've seen example code that declares parameters using the generic wildcard Class<?>.
Why not just use Class without the generic wildcard? Can't Class represent all possible class types including generics? What is the benefit, if any of using Class<?> in my situation?
The accepted answer in an existing related question (see below) does not actually provide a useful answer.
What does Class<?> mean in Java?
The main difference lies in the (self-)documentation of the code to the reader. A variable declared as Class<?> says: “the actual type represented by this Class instance is unknown or not representable at compile-time and I know that”. In contrast the type Class says: “I’m not using Generics here”, perhaps, because you don’t know that Class is generic, or you are a bit sloppy, didn’t understand the difference between Class<?> and Class, or this is very old pre-Generics code.
This has consequences for the code. E.g.
Class<?> unknownType=Object.class;
Class<String> string=unknownType;
produces a compile-time error as you are assigning an explicitly unknown type to a variable declaring a known type.
In contrast
Class nonGenericType=Object.class;
Class<String> string=nonGenericType;
will only produce a (suppressible) warning as you are performing a non-generic, aka unchecked, operation.
Regarding what you can do with a Class object, besides assignments, there is no difference, as, when you use it to create a new instance, the compile-time type of the returned reference will be the most abstract type there is, java.lang.Object, in both cases. Had Class methods receiving arguments related to the type parameter, a difference showed up as you can’t invoke such methods for an unknown type, Class<?> (it would be the same as trying to insert an element into a List<?>) while you could invoke such a method unchecked on a raw Class instance. But since there are no such methods, there’s no difference in functionality between Class<?> and Class.
Still, you should always use Class<?> to be sure that no accidental unchecked operations, like the assignment shown above, happen. If using Class doesn’t produce compiler warnings, you should check how to (re-)enable them. If the compiler silently ignores raw types or unchecked operations, there might be other problems, with other types than Class, hiding somewhere.
The difference between the wildcard type <?> and the raw type in this particular scenario is only whether the compiler will warn you or not. Otherwise they're equivalent, so if for some reason you don't wouldn't want to use the <?> syntax and you didn't care about compiler warnings, you could use the raw type without any problems.
Netbeans not complaining about the raw type is not correct behaviour, and my Eclipse will complain when using a raw Class.
The Class object has distinct usage patterns, which affect whether the type will be a concrete type (seen in method parameters as Class<T> clazz) or the wildcard Class<?>.
The most common form seen in the API is the concrete type, since it allows you to use newInstance() (primarily) in a type-safe way (making all Class<T> objects automatically type-safe factories), such as the following:
public static void List<T> fill(Class<T> clazz, int size) {
List<T> l = new ArrayList<T>();
for(int i = 0;i < size; i++)
l.add(clazz.newInstance());
return l;
}
So Class<T> is useful, but what about Class<?>? Well, not so much. As indicated at the beginning, it's just required for syntax compliance. The alternative would be to use a concrete T type redundantly.
public void foo(Class<?> clazz) {
// Do something non-typed, we don't have a type
}
vs.
public <T> void foo(Class<T> clazz) {
// Do something non-typed, even though we have a type T
}

Is it useful to provide a type-safe method?

I'm designing the interface as follows:
public interface Parameters {
public <T> T getValue(ParameterName pn, Class<T> valueType) throws ClassCastException;
}
An implementation is obligated to throw ClassCastException if the Class instance of the value to be returned is not an assignableForm of the Class passed as a parameter.
Does it make sesnse? It provides compile-time type-safety, but we can do the same with just explicit cast.
Or it's much better to declare just
public Object getValue(ParameterName pn)
leaving all class-cast issues to the client.
I have used this form of API where I add the ability to convert the type to the one desired. e.g. if it's a String but you need an Integer it will attempt to parse it.
Otherwise, as you suggest you are not adding much that this method doesn't provide.
public <T> T getValue(ParameterName pn);
This avoid needing an explicit cast.
It is a misunderstanding that you gain any compile-time type-safety by passing the Class object of the expected return type as a parameter. If the client passes a Class of the wrong type the error will only get detected at runtime.
But I think the design with a Class parameter has other advantages:
The parameters creates a natural place to document the behaviour of the method regarding the return type.
You can write code in the method to check the Class parameter and provide a specific and meaningful error message if the user makes a mistake.
It is very visible in the calling code and brings attention to the behaviour of the method.
I can think of two disadvantages of that design:
The existence of the parameter might give users the impression that the parameter affects the return value of the method.
It is more verbose than using an unrestricted generic return type as Peter suggests in his answer.

Java how to parametrize a generic method with a Set?

I have a method with such signature:
private <T> Map<String, byte[]> m(Map<String, T> data, Class<T> type)
When I invoke like this for example it is working fine:
Map<String, String> abc= null;
m(abc, String.class);
But when my parameter T is a Set it doesn't work:
Map<String, Set<String>> abc= null;
m(abc, Set.class);
Is there a way to make it work?
You're going to have to do something really ugly, using an unchecked cast like this:
m(abc, (Class<Set<String>>) (Class<?>) Set.class);
This comes down to type-erasure. At runtime Class<Set<String>> is the same as Class<Set<Integer>>, because we don't have reified generics, and so there is no way to know that what you have is a class for a "Set of strings" vs. a class for a "Set of integers".
I asked a related question some time ago that should also give you some pointers:
Return a class instance with its generic type
IMO this confusion is due to the fact the generics were bolted on after the fact, and aren't reified. I think it's a failing of the language when the compiler tells you that the generic types don't match, but you don't have an easy way of even representing that particular type. For example, in your case you end up with the compile-time error:
m(abc, Set.class);
^
required: Map<String,T>,Class<T>
found: Map<String,Set<String>>,Class<Set>
reason: inferred type does not conform to equality constraint(s)
inferred: Set
equality constraints(s): Set,Set<String>
where T is a type-variable:
T extends Object declared in method <T>m(Map<String,T>,Class<T>)
Now it would be perfectly reasonable for you to think "Oh, I should use Set<String>.class then", but that is not legal. This is abstraction leakage from the implementation of generics in the language, specifically that they are subject to type-erasure. Semantically, Set<String>.class represents the runtime class instance of a set of strings. But actually at runtime we cannot represent the runtime class of a set of strings, because it is indistinguishable from a set that contains objects of any other type.
So we have a runtime semantic that is at odds with compile-time semantic, and knowing why Set<T>.class isn't legal requires knowing that generics are not reified at runtime. This mismatch is what leads to weird workarounds like these.
What compounds the problem is that class instances also ended up being conflated with type-tokens. Since you do not have access to the type of the generic parameter at runtime, the work around has been to pass in an argument of type Class<T>. On the surface this works great because you can pass in things like String.class (which is of type Class<String>) and the compiler is happy. But this method breaks down in your case: what if T itself represents a type with its own generic-type parameter? Now using classes as type-tokens is not useful because there is no way to distinguish between Class<Set<String>> and Class<Set<Integer>> because fundamentally, they are both Set.class at runtime and so share the same class instance. So IMO, using a class as a runtime type-token doesn't work as a general solution.
Due to this shortcoming in the language, there are some libraries that make it very easy to retrieve the generic type-information. In addition they also provide classes are better at representing the "type" of something:
TypeTools
Reflection Explained: Google Guava
The following signature works with super keyword. (I tested with Java7)
private <T> Map<String, byte[]> m(Map<String, T> data, Class<? super T> type)
Map<String, Set<String>> abc = null;
m(abc, Set.class);
This is subtyping for generics.
From what I see, there are two potential solutions to this problem in which both have their respective limitations.
The first solution relies on the fact that java's type erasure is complete, meaning that types for any parametrized types are erased regardless of "depth". For example: a Map<String, Set<String> will get reduced to Map<String, Set> and then Map<Object, Object> meaning that whilst type information is hard to obtain, it technically isn't needed during runtime given that any object can be inserted into the Map (given that it passes all class casts).
With this, we can create a relatively "ugly" (compared to the second solution) method of obtaining runtime type information through an instance present in the map. By doing so, regardless of how many sets you embed and what the resultant "type" is present after erasure, we can guarantee that an instance of it will be insertable back into the original map.
Demonstrated below:
// Java 7 approach
private <T> Map<String, byte[]> m(Map<String, T> data){
Class valueType = null;
Iterator<T> valueIterator = data.values().iterator();
while(valueIterator.hasNext()){
T nextCandidate = valueIterator.next();
if(nextCandidate != null){
valueType = nextCandidate.getClass();
break;
}
}
if(valueType == null){
// No instance present, fail
return null;
}
// Create a new instance
T obj = (T) valueType.newInstance(); // Exception handling not shown
// Rest of code here
return null;
}
as seen, the type information is extracted directly from the first non-null value present within the map. Under java 8 we can do better using streams:
// Java 8 approach
private <T> Map<String, byte[]> m(Map<String, T> data){
// Note: use findFirst() for more consistent behaviour
Optional<T> optInstance = data.values().stream().filter(Objects::nonNull).findAny();
if(!optInstance.isPresent()){
// No instance present, fail
return null;
}
Class valueType = optInstance.get().getClass();
// Create a new instance
T obj = (T) valueType.newInstance(); // Exception handling not shown
// Rest of code here
return null;
}
However, this solution has a couple of limitations. As stated, the map has to contain at least one non-null value for the operation to be successful. And secondly, this solution doesn't take account of subclassing of the declared type (? extends T) on specific elements which may provide to be problematic if you have elements of different classes (e.g. TreeSet and HashSet within the same map).
The second issue can be solved easily by dealing with type information on a key-value pair basis rather on a "whole" map basis though this comes at the cost of "knowing" the type information for all elements within the map. Alternatively, more complex solutions such as devising the most specific common superclass to all non-null values within the map can also be used, but for all intents and purposes, this becomes more of a guesstimate solution than a real one.
The second solution to this problem is, in my opinion, a lot cleaner but poses additional complexity to the caller. This approach follows a more functional approach and can be applied if there are only a limited number of type-dependent operations within the method. Following your proposed case of instantiation of the generic type T, we can modify the method as follows:
private <T> Map<String, byte[]> m(Map<String, T> data, Callable<T> creator){
// Create a new instance
T obj = creator.call(); // Exception handling not shown
// Rest of code here
return null;
}
and called as follows:
Map<String, Set<String>> data = new HashMap<>();
// Instantiation method set to new HashSet (thanks to bayou.io for HashSet::new)
m(data, HashSet::new); // Note: replace with anonymous inner class for java 7
in this case, the type information (which is present at the level of the caller) can be bypassed by having the caller provide the type-dependent functionality required. The example provides a basic HashSet creation for all values but more complex instantiation rules can be defined on a per-element basis.
The downside to this approach is that it provides complexity to the caller and can be very bad if this were to be an external API function (though the use of private within your original method suggests otherwise). Java 7 and below also causes quite a bit of boilerplate anonymous inner class code to pop up making caller-side code harder to read. Additionally, if most of your method requires type-information to be present then this solution is less feasible as well (since you'd be reprogramming most of your method on a per-type basis, defeating the point of using generics).
In all, I'd personally prefer to use the second approach if possible, only using the first approach if deemed infeasible. The gist of the solutions I'm getting at here is to not rely on type information when dealing with generics or at least set a bound such that you get functionality you require without ugly hacks. In the case where type-dependent operations have to be performed, have the caller provide the functionality for that (through Callables, Runnables or some FunctionalInterface of your creation).
If type information is absolutely critical for some reason not made apparent, I suggest reading this article to stop type erasure altogether, allowing type information to be present directly from within the method.
You'd need to do it like :
Map<String, Set> abc = null; //gives a compiler warning
m(abc, Set.class)
The issue is that if you want T to be captured to Set<String>, there will be no way to express Class<T> since there's no such thing as Set<String>.class, just Set.class.

How to do `MyClass<String>.class` in Java?

How can call public <T> T doit(Class<T> clazz); using MyClass<String>.class as clazz where I can not instantiate or extend MyClass.
EDIT: 'David Winslow' and 'bmargulies' responses are correct (MyClass<String>) doit(MyClass.class); works for the original question BUT surprisingly when the method returns say MyClass<T> instead of T casting will not compile any more.
Edit: I have replaced List with MyClass and added the condition to my original question.
Use List.class. Because of type erasure type parameters to Java classes are entirely a compile-time construct - even if List<String>.class was valid syntax, it would be the exact same class as List<Date>.class, etc. Since reflection is by nature a runtime thing, it doesn't deal well with type parameters (as implemented in Java).
If you want to use the Class object to (for example) instantiate a new List instance, you can cast the result of that operation to have the appropriate type parameter.
List<String> list = (List<String>)(ArrayList.class.newInstance());
I've seen similar questions asked several times, for example
Acquiring generic class type
There are legitimate reasons to construct static generic types. In op' case, he would probably like to
MyClass<String> result = doit(MyClass<String>.class);
Without language syntax support, casting is the correct way to go. If this is needed quite often, the casting should be put in a method, as
public class MyClass<T>
{
#SuppressWarnings("unchecked")
// may need a better method name
static public <T2> Class<MyClass<T2>> of(Class<T2> tClass)
{
return (Class<MyClass<T2>>)(Class<?>)(MyClass.class);
}
}
MyClass<String> result = doit(MyClass.of(String.class)); // no warning
We can supress the warning on that method alone, after making sure the cast is safe. Any call site will not see the warning.
This is all compile time casting game. At runtime all the type parameters are erased, and really only the naked class object is passed around. The of method will most likely be optimized off, so to JVM the last line is nothing but
MyClass result = doit(MyClass.class)
There are also times when at runtime we need a complete MyClass<String> type. A ParameterizedType object needs to be obtained to represent MyClass<String>.
When the two requirements are combined together, that is, we need a compile time expression regarding MyClass and String that will evaluate at runtime to a ParameterizedType
ParameterizedType type_MyClass_String = ???? MyClass ?? String ???
There is a technique involving an anonymous subclass of MyClass<String>
ParameterizedType type_MyClass_String = superTypeOf( new MyClass<String>(){} );
which I find quite disturbing.
See http://jackson.codehaus.org/1.7.0/javadoc/org/codehaus/jackson/type/TypeReference.html and the references that it references for a comprehensive discussion of the issues around generics.
the bottom line is that, if you really want to work with generic types in this way, you have to stop using Class and start using Type and its subclasses.
Contrary to your comment on another answer, you can write List<List<String>> obj = (List<List<String>>) doit(List.class);, you just can't avoid a warning when you write it.
Since after your update your question does not appear to be an exact duplicate:
You would need to call getClass() on an instance of MyClass. Better have a dummy static final instance somewhere:
public static final MyClass INSTANCE = new MyClass();
...
return (Class<MyClass<String>>) instance.getClass();
T corresponds to List, so any reference to String as the generic paramter of List is irrelevant.
How to do MyClass<String>.class in
Java?
You can't.
Generics in Java use type erasure; the type of the parametrized argument is enforced during compilation, but it is lost after compilation. The resulting byte code for an instance of a generic class does not contain any run-time meta-data on its arguments whatsoever.
As it is now, it is just not possible, a major language design blunder IMO.

Categories

Resources