Is it possible in Java to forbid certain superclasses in generics? - java

I would like to ask if it is possible in Java 8+ to declare a generic bound T so that it extends superclass/superinterface U (which could be Object, or Serializable) but breaks compilation if T extends L (which must extend U first).
I have found this problem using filter range objects: one of my developers invoked the wrong method and spent much time questioning on why it was giving inconsistent results. So I wanted to help her changing the method signature, someway, to early detect that she is using wrong code.
I will display my example case in a very simplified way. We are talking about tables and dynamic filters.
#Displays a text "[field name] is equal to [value]"
#Value (T) must be Oject
#Internally uses Object::toString
#Null shows blank string
public static String <T> localizeEq(Localizable fieldName, T value);
<LocalDate> localize(forI18nLabel("DATE_OF_BIRTH_LABEL",dateOfBirth)
"Date of birth equals 01/01/1900" (en)
"syntymäaika on 01/01/1990" (fi)
#Additional diplays for "ge, gte, le..."
#Overload methods not displayed
#SimpleFilter is {op:"ge|ge|eq...",value:""}}
#The effective display depends on the op attribute
#Example "[field name] is [operator] [value]"
#Example "[field name] is less or equal than [upper]"
#If <filter != null but filter.op == null || filter.value> the method returns null
public static String <T> localize(Localizable fieldName, SimpleFilter<T> filter)
#localize(forI18nLabel("SALARY"),salaryFilter)
#salaryFilter = {op:"lt",value:10000}
#Salary is less than 10000 (en)
Now the problem is that the the upper bound U of my generics is Serializable and developer inadvertently invoked localizeEq, which accepts atomic values, with a parameter of type SimpleFilter<?> that extends Serializable. The method localizeEq builds a filter text "[field name] is equal to {op:null,value:null}".
The main issue is the null check. Methods that operate on atomic values (e.g. localizeEq, localizeNe) check if the parameter is null. Methods that operate on complex filters check that either the filter parameter is null or its value is null before going on.
That is the reason of the question. Obviously I can (will amend my code in order to) inspect the type of the value parameter when the method is invoked but has three drawbacks:
Developers find it only at runtime
Developers find the problem only when value is not null
Nobody in my company runs automated tests, so they will find out only when kickstarting the entire application and setting a non-null value to the filter. Once a manual test is done, it is never repeated
[Edit]
For my specific case there is another trick, but it involves creating more than a dozen overloaded deprecated methods:
#Deprecated
public static String localize[Eq|Ne...](Localizable fieldName, SimpleFilter<?> value){ throw new UnsupportedOperationException("Wrong method");}
[Edit 3]
The code is on Gist. Please note that in repository code we statically import SimpleFilter.filter or LocalDateRangeFilter.filter methods. In the question it is assumed that localize(Localizable,SimpleFilter) is part of the same class than other methods. And please note there are a few other *RangeFilter classes to support Joda Time, Java Util Date and NumericRange in our repository. They all suffer same issue.
I would like to focus anyway on the scope of the question: forbidding extension in generic, which seems not possible in the JLS.

I would like to ask if it is possible in Java 8+ to declare a generic
bound T so that it extends superclass/superinterface U (which could be
Object, or Serializable) but breaks compilation if T extends L (which
must extend U first).
The T in your pseudocode seems to be a type parameter, not a bound. Bounds are something different, and in fact, putting a bound on T seems to be what you are asking about. Indeed, without one -- in particular, without a lower bound -- your localizeEq() method is not gaining anything from being generic. As it stands, that method would be clearer if you just got rid of T altogether, and declared the second parameter to be of type Object (which would be equivalent to the current code) or Serializable or whatever.
I take it that the method was made generic in hopes of somehow using its type parameter to exclude arguments of certain subtypes, but that is not possible in Java, because
lower type bounds are inclusive, not exclusive
lower type bounds limit types meeting the bound to a single line of inheritance, which seems inconsistent with your intent
Now the problem is that the the upper bound U of my generics is
Serializable and developer inadvertently invoked localizeEq, which
accepts atomic values, with a parameter of type SimpleFilter<?> that
extends Serializable. The method localizeEq builds a filter text
"[field name] is equal to {op:null,value:null}".
If one is not supposed to pass a SimpleFilter to the localizedEq() method then I'd say that you have a design flaw here. You could catch violations at runtime, of course, but the type system does not provide a way to express the compile-time constraint you're looking for.
For my specific case there is another trick, but it involves creating more than a dozen overloaded deprecated methods:
Indeed, overloading is probably the best available solution, but I would suggest approaching it from the other direction. Instead of adding overloads for localizeEq, localizeNe, etc., deprecate the existing versions of those methods and instead overload localize with a version or versions that provide the wanted behavior for arguments that are not SimpleFilters.

Related

Mix generics with non-generics in class signature

I see similar questions asked for C++ and C# but not for Java. Is there a way I can define concrete types along with generics in a class signature? Something like this:
import java.util.Optional;
class Foo<T, Optional<O>> {
}
Compiler describes this as invalid syntax.
You're mixing up the declaration of a type variable and the usage of one.
In class Foo<T> {}, as well as public static <T> void foo() {}, the <T> part is declaring the types. It's equivalent to the x in public class Foo { int x; } - you are saying that there is a type variable and it has the name T, you're not using it.
Hence, Optional<O> is obviously illegal there. Just like int a + b is gobbledygook.
You CAN put a bound on your typevar; just like Number a doesn't just declare an a but also limits it to be a reference to some instance of Number (or null), you can do the same when you declare types: class Foo<T extends Number> puts a bound on T, decreeing that it must necessarily be Number or some subtype thereof.
Thus, in theory, you could do something like:
class Foo<T, O extends Optional<?>> {}
except that makes no sense, there's no reason to ever write that code. What is your intent? That the second typevar is optional? That doesn't work in java, unfortunately. No such thing as optional typevars.
That it can be an Optional<T>? Just T is any type, including Optional, so that isn't needed.
That it must be an Optional<T>? That's 'optional abuse'. Optional isn't type-orthogonal and thus isn't that kind of flexible. You mustn't write code that way. Optionals can make sense as the return type of a method and shouldn't be used anywhere else; parameters should never be typed that way, fields should never be typed that way, and they definitely should never show up in generics. Any code that calls a method that returns an optional should immediately 'unwrap' it (e.g. by chaining an .orElse call).
The language spec doesn't enforce it, but the language gives you zero tools to deal with optional dichotomy. Essentially that means any API or code that works that way is going to lack abstractive powers to an incredible degree, and would feel exceedingly clunky as a result.
For example, it is impossible to write a method in java that accepts either List<String>, or List<Optional<String>>, but not e.g. List<Optional<Number>>, and can operate on the provided list regardless. Eventhough that is an operation that makes perfect sense: You can trivially have a method that for example only reads from the list (never calls .add or .addAll or whatnot on the list), and when it reads, does nullchecks (which would be pointless for a list that contains never-null strings, but it doesn't harm anything). Or, the method does invoke .add, but always adds non-null things. adding a guaranteed not null string is a legal move on a 'list of never null strings' and also a legal move on 'list of could-be-null string refs', and yet, java doesn't let you write this method: That's why you shouldn't treat optional as a type-in-flight; APIs cannot deal with the dichotomy.
And yet, you can't write this in java or just about any other language that supports Optional. That's just one example, there are many, they all point to the same thing: Optional? Only sensible as return values from methods, and must be unwrapped immediately; any other usage of them is harmful and silly: Leads to bugs and code that is hard to maintain, and isn't particularly flexible (is hard to adapt to changing requirements).

Java Class.cast to most specific with method overloading

I have a code that looks like this
function a(Object m) {}
function a(BasicDbObject) {}
function a(TypeA) {}
function a(TypeB) {}
function a(TypeC) {}
.....
function b(Object m) {
// Some function using Java reflection to determine class of Object m
Class X = c(m);
a(X.cast(m));
}
Here is the problem. It always execute a(Object m) rather than a(BasicDbObject m), even it is BasicDbObject.
My end goal is to execute most closest function to the object passed.
What you are trying cannot be done, because Java is statically typed, and the method overload is resolved at compile-time, not run-time.
The only way to resolve the overload at runtime, is for the method call itself to be done with reflection.
Serious non-answer: wrong approach.
You don't use reflection to dynamically determine a type, to then figure which overloaded method to call.
Instead, use polymorphism. Meaning: don't overload, but override.
Rest assured: getting "reflection" working is hard. Getting it correct, and robust and stable is a super challenging, uphill battle.
You basically want to invent your own personal dynamic dispatch implementation. Unless you have super hard pressing reasons to do so, that is a terrible idea. Because chances are that you will get it wrong. Many many times. And even when your code is working, there will be many incidents later on, when unforeseen things happen in production.
As said: don't do this. Don't fight the language, instead use the means that the language offers you to solve such problems: an inheritance tree of classes, and polymorphic methods. Then let the JVM decide which method to invoke. Most likely, the JVM will do a much better job, compared to what you will come up with.
function a(Object m) {}
function a(BasicDbObject) {}
When methods are overloaded, it may not be intuitive to know the method which gets invoked for any set of parameters because, unlike the situation with overridden methods, the method overloading that gets invoked is determined at compile time (i.e. statically) rather than at run time (i.e. dynamically). This behavior is confusing because overriding methods is more common and this sets our expectations for method invocation.
There are some rules for doing method overloading as robustly and as simply as possible. These are all nicely enumerated in Effective Java (J. Bloch, 2nd and 3rd eds.).
Your situation is made complex because:
You have two overloadings with the same number of parameters whose types are not radically different ... and ...
The behavior of the overloadings is apparently dependent on the type of the parameter (if the behavior was identical, then you simply have one overloading forward to the other)
When this situation arises, you should try to correct it by giving the overloadings different names. It should always be possible to do this and doing so often improves the clarity and maintainability of the code.
If this can't be done for any reason, then the best workaround is to replace the overloadings with a method that accepts the most general parameter type and which invokes helper methods based on the most specific type of the passed argument.
So instead of the above, you can get the behavior you want by using...
public Function a(Object m) {
if (m instanceof BasicDbObject) return doDbObject(m);
if (m instanceof OtherDbObject) return doOtherDbObject(m);
return doGenericObject(m);
}
Note that this isn't the code that you would use when Java adopts pattern matching in the language. Note also that the effect of this code is to give your overloadings different names, but the selection of the distinct method is made at run time using instanceof comparisons rather than at compile time by simply using a distinct name.
TLDR; if you are doing method overloading in a circumstance in which the parameter types are not (or may not be) radically different then you are better off not overloading and using distinct method names.

Using Raw types to dynamically apply type parameters for a reflected subclass

IMPORTANT:
the code I currently have Is working per my expectations. It does what I want it to do. My Question is about wether the WAY in which I have made it work is wrong. The reason I am asking this is because I've seen plenty of stack overflow results about raw types and how they should basically NEVER be used.
What I'm doing and Why I used raw types
Currently I am dynamically creating a concrete subclass of a generic interface where the interface takes in parameters when the class is constructed. When I make an instance of this class and use its returned object to call various methods, I use raw types because it works for what I'm trying to do. Here is an example in my functioning code where the raw types are used. This code is in top down order i.e. between code blocks there is no code.
Loading properties file
Properties prop = new Properties();
try {
prop.load(ObjectFactory.class.getResourceAsStream("config.properties"));
This is the File parser that implements FileParserImplementation and takes in the data and puts it into an array. This code gets the Class type and then makes an instance of that type dynamically.
Class<? extends FileParserImplementation> parser = null;
parser = Class.forName(prop.getProperty("FileParserImplementation")).asSubclass(FileParserImplementation.class);
FileParserImplementation ParserInstance = (FileParserImplementation) parser.getDeclaredConstructors()[0].newInstance();
These two classes and their instances are the two seperate DataParsers implementing DataParserImplementation. These take in the array of Strings that the FileParser gives and creates objects/manipulates the data into whatever is needed. It puts out a Collection of this data. The Fileparser dependency is passed in through constructor injection. This can be configured through the properties file at runtime.
Class<? extends DataParserImplementation> dataset1 = Class.forName(prop.getProperty("DataParserImplementation_1")).asSubclass(DataParserImplementation.class);
Class<? extends DataParserImplementation> dataset2 = Class.forName(prop.getProperty("DataParserImplementation_2")).asSubclass(DataParserImplementation.class);
DataParserImplementation Dataset1Instance = (DataParserImplementation) dataset1.getDeclaredConstructors()[0].newInstance(ParserInstance);
DataParserImplementation Dataset2Instance = (DataParserImplementation) dataset2.getDeclaredConstructors()[0].newInstance(ParserInstance);
This is the Crossreferencer class that implements CrossReferencerImplementation. It takes in the two datasets and Cross references them In whatever way is desired by the actual concrete reflected class. This also can be configured at runtime. It outputs a Map in this main.
The map serves as the final collection for the data (I might change that later).
Class<? extends CrossReferenceImplementation> crossreferencer = Class.forName(prop.getProperty("CrossReferenceImplementation")).asSubclass(CrossReferenceImplementation.class);
CrossReferenceImplementation crossReferencerInstance =
(CrossReferenceImplementation) crossreferencer.getDeclaredConstructors()[0].newInstance();
Getting the Map result from calling a method on our reflected instance. Then the contents of this map are printed out. currently it seems the map parameters are gotten as well because the Objects that are inside the map are properly using their toString methods when reflectiveFinalMap.get(key).toString() is called.
This leads me to believe it works as I intend.
Map reflectiveFinalMap = (Map)
crossReferencerInstance.CrossReference(Dataset1Instance.Parse(), Dataset2Instance.Parse());
for (Object key:reflectiveFinalMap.keySet()) {
System.out.println(key + " { " +
reflectiveFinalMap.get(key).toString() + " }");
}
return reflectiveFinalMap;
}
//catch block goes here
Notice that each time I reflectively create an instance of a class that implements one of my interfaces, I use the interface as the raw type. My Hope is that the reflection then sees the parameterized type of this raw type when it creates the concrete subclass, because thats where the parameter types are actually specified. The point is to let any class that implements those interfaces be generic to the point where they can take in just about anything and return just about anything.
Things I tried to not use raw types.
I've tried to actually obtain the parameterized type of CrossReferenceImplementation in the reflected crossreferencer Class that I get right now by calling
Class arrayparametertype = (Class)((ParameterizedType)crossreferencer.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
And then I tried to pass in that arrayparameter when creating an instance of crossreferencer like this:
CrossReferenceImplementation crossReferencer = (CrossReferenceImplementation<<arrayparametertype>>) crossreferencer.getDeclaredConstructors()[0].newInstance();
That didn't work since variable parameter types apparently aren't a thing.
I tried to manually specify the specific parameter of the concrete reflected class(I DON'T want this anyway because it breaks the whole point of reflection here, decoupling the Classes from each other by being able to use anythng that implements the appropriate interface). This caused this warning to appear and the code to not actually run the methods it was supposed to:
//how the parameters were specified. Messy and breaks the reflection.
CrossReferenceImplementation<Map<String, SalesRep>,Map<String, SalesRep>,Map<String, SalesRep>> crossReferencer = (CrossReferenceImplementation) crossreferencer.getDeclaredConstructors()[0].newInstance();
//where the warning occured
Map reflectiveFinalMap = (Map) crossReferencer.CrossReference(Dataset1.Parse(), Dataset2.Parse());
The Warning:
"Dataset1 has raw type so result of Parse is erased".
Note that SalesRep here is the object in which the data is held as fields of that object. This object gets manipulated and put into various collections. It too is accessed via reflection in the many methods of DataParserImplementations
A similar error message and problem occured when specifying the parameter type of the Map (AGAIN I DON'T want this because it makes the reflection pointless I want the map return result to be generic and be specified by the implementing class).
//where the parameterized type was specified
Map reflectiveFinalMap = (Map<String,SalesRep>) crossReferencer.CrossReference(Dataset1.Parse(), Dataset2.Parse());
When specifying the actual parameterized type of the map result the error message was:
"crossReferencer has raw type so result of CrossReference is erased".
Running the code did indeed confirm for me that .CrossReference method's results were erased while everything else ran fine.
What internet searches I tried before asking here
So I used the raw types for both operations As can be seen in the main code and everything worked fine. But I have seen so much "Don't use raw types". And this is why I ask: Is this an appropriate use of raw types? Should I do it a different way that DOESN'T break the reflection? It breaks the reflection because manually specifying the type parameter not only makes my code not run, it also means ONLY that concrete class can be used. I reflected so that I could use anything that implements the generic interface. I don't want to only be able to use specific concrete instances. I've tried searching stack overflow for whats in my title and other similar things. I think this might be related to type erasure but I'm honestly not sure of that. Nothing else really addressed this problem because nothing talked about generics, parameterized types and reflection all at once (the crux of my problem). I have been told generics and reflection don't play well together but this code works anyways and works the way I want it to. It works well. I just want to make sure I'm not doing something TERRIBLY wrong.
The Goal.
To gain an Understanding of my current usage of raw types so I know I'm doing it the right way. By 'Right' I mean the opposite of what I define as the 'Wrong' Way below. An example of what 'Understanding' I seek is:
To understand why puesdo code along the lines of:
ConcreteClass forname(myPropertiesFileObject.get(ConcreteClassname)) as subClass of (MyGenericInterface);
MyRAWGenericInterfaceType ConcreteClassInstance = (MyRAWGenericInterfaceType) ConcreteClass.newInstance( Insert generic Type constructor arguments here);
RAWCollectionType someCollection = RAWCollectionType concreteClassInstance.CallingAMethod(Insert generic Type method arguments here);
Uses Raw types where RAW is contained in the Interface or collection type name. This is as opposed to doing it in some way that doesn't use raw types but doesn't break the point of the reflection, to decouple the interactions between these classes. Specifying the parameters with hard code would 'break the reflection' in this case. Additionally I'd like to understand Why specifying parameters (even if I know thats not what I'm going to do) for these RAW types in the pusedocode above causes the errors listed above in the question, Namely why is the result of CallingAMethod erased when supplying the actual parameters to the RAWCollectionType that the method returns? The root problem is that when I supply type parameters to RAWCollectionType when I declare it, it refuses to be updated by what CallingAMethod returns and I Don't Understand Why. It takes the return value, but if the body of the method CallingAMethod has the returned value passed in as an argument, updated inside the method and then returned, the return that I receive doesn't have the updates. CallingAMethod in this example would be like if I hada list like:
[1,2,3]
and inside the method I had something like:
foreach(thing in list){
thing += 1
}
and then I returned the list, the return I'd get when specifying parameters would be [1,2,3] and when using raw types it would be [2,3,4] like I desire. I'm asking this because I've heard bad things about using raw types.
Additionally I want to make sure that my use of raw types is not horribly wrong and that it works because it's SUPPOSED to work. Maybe I've just gotten good at the whole reflection and generics thing and found a valid use for raw types, or I could be doing something so horrible it warrants my arrest. Thats what i intend to find out. To clarify, by wrong I mean:
bad design (should use a different way to call my methods reflectively and also use reflective classes that use generic interfaces)
inefficient design(time complexity wise, code line wise or maintainability wise)
there is a better way, you shouldn't even be doing this in the first place
If any of those reasons or something I missed popped out when you read this code then TELL ME. Otherwise please explain then why my use of raw types is Valid and isn't a violation of this question:[link]What is a raw type and why shouldn't we use it?
Java have type erasure, so your Map<A,B> in runtime is just a Map, same for CrossReferenceImplementation<Map<String, SalesRep>,Map<String, SalesRep>,Map<String, SalesRep>> is just a CrossReferenceImplementation.
This also means that you can cast any map to Map and just put any objects you want in it, so you can have Map<String, Long> that is actually storing objects of Map<Cookie, Fish> type, and this is why you need to be careful with raw types and reflections.
You can't really use reflection and generics normally - you will always have some unchecked code then, but you can limit it to minimum and make it kind of type-safe anyways.
Like you can create own method to get field: (this is a bit of pseudocode, I will skip all possible exceptions, etc)
public class FieldAccessor<O, T> {
final Field field; // + private constructor
public T get(O object) { return (T) field.get(object); } // unsafe, bu we validated this before constructing this accessor
public static <O, T> FieldAccessor<O, T> create(Class<? super O> definingClass, Class<? super T> fieldClass, String fieldName) {
Field field = definingClass.getDeclaredField(fieldName);
if (field.getType() != fieldClass) {
throw some exception;
}
return new FieldAccessor<>(field);
}
Then you have all the needed validation before you need to use that field, and it will already return valid type. So you can get some value of valid type and add it to normal generic Map instance.
FieldAccessor<X, A> keyAccessor = FieldAccessor.create(X.class, A.class, "someProperty");
FieldAccessor<Y, B> valueAccessor = FieldAccessor.create(Y.class, B.class, "someOtherProperty");
Map<A, B> myMap = new HashMap<>();
mapMap.put(keyAccessor.get(myXValue), valueAccessor.get(myYValue));
This way you have type safe code that still works on reflections - it might still fail at runtime if you will provide invalid types, but at least you always know where it will fail - as here FieldAccessor is already checking all the types in runtime to ensure that you will not do something stupid like add Integer to Map<String, Long> as this might be hard to debug later. (unless someone will use this accessor as raw type, as .get isn't validated - but you can add that by passing definingClass to constructor and checking object instance in get methods)
You can do similar stuff for methods and fields that use generic types (like field of Map<X, Y> type, this FieldAccessor would only allow you to check if it is some kind of Map) - but it would be much harder as API for generics is still a bit "empty" - there is no build in way to create own instances of generic types or to check if they are assignable. (libraries like gson does that so they can deserialize maps and other generic types, they have own implementation of java generic type representation interfaces, like that ParameterizedType and implemented own method to check if given types are assignable)
Just when you are using reflections you need to always remember and understand that you are the one responsible for validating types, as compiler can't help you here, so that unsafe and raw typed code is fine as long as you have logic that validates if this code will never do something really unsafe (like that passing wrong type to generic method, like Integer to map of Long).
Just don't throw raw types and reflections in the middle of some normal code, add some abstraction to it, so it will be easier to maintain such code and project.
I hope this somewhat answers your question.

Inconsistent get/put methods of Map [duplicate]

What are the reasons behind the decision to not have a fully generic get method
in the interface of java.util.Map<K, V>.
To clarify the question, the signature of the method is
V get(Object key)
instead of
V get(K key)
and I'm wondering why (same thing for remove, containsKey, containsValue).
As mentioned by others, the reason why get(), etc. is not generic because the key of the entry you are retrieving does not have to be the same type as the object that you pass in to get(); the specification of the method only requires that they be equal. This follows from how the equals() method takes in an Object as parameter, not just the same type as the object.
Although it may be commonly true that many classes have equals() defined so that its objects can only be equal to objects of its own class, there are many places in Java where this is not the case. For example, the specification for List.equals() says that two List objects are equal if they are both Lists and have the same contents, even if they are different implementations of List. So coming back to the example in this question, according to the specification of the method is possible to have a Map<ArrayList, Something> and for me to call get() with a LinkedList as argument, and it should retrieve the key which is a list with the same contents. This would not be possible if get() were generic and restricted its argument type.
An awesome Java coder at Google, Kevin Bourrillion, wrote about exactly this issue in a blog post a while ago (admittedly in the context of Set instead of Map). The most relevant sentence:
Uniformly, methods of the Java
Collections Framework (and the Google
Collections Library too) never
restrict the types of their parameters
except when it's necessary to prevent
the collection from getting broken.
I'm not entirely sure I agree with it as a principle - .NET seems to be fine requiring the right key type, for example - but it's worth following the reasoning in the blog post. (Having mentioned .NET, it's worth explaining that part of the reason why it's not a problem in .NET is that there's the bigger problem in .NET of more limited variance...)
The contract is expressed thus:
More formally, if this map contains a
mapping from a key k to a value v such
that (key==null ? k==null :
key.equals(k)), then this method
returns v; otherwise it returns null.
(There can be at most one such
mapping.)
(my emphasis)
and as such, a successful key lookup depends on the input key's implementation of the equality method. That is not necessarily dependent on the class of k.
It's an application of Postel's Law, "be conservative in what you do, be liberal in what you accept from others."
Equality checks can be performed regardless of type; the equals method is defined on the Object class and accepts any Object as a parameter. So, it makes sense for key equivalence, and operations based on key equivalence, to accept any Object type.
When a map returns key values, it conserves as much type information as it can, by using the type parameter.
I think this section of Generics Tutorial explains the situation (my emphasis):
"You need to make certain that the generic API is not unduly restrictive; it must
continue to support the original contract of the API. Consider again some examples
from java.util.Collection. The pre-generic API looks like:
interface Collection {
public boolean containsAll(Collection c);
...
}
A naive attempt to generify it is:
interface Collection<E> {
public boolean containsAll(Collection<E> c);
...
}
While this is certainly type safe, it doesn’t live up to the API’s original contract.
The containsAll() method works with any kind of incoming collection. It will only
succeed if the incoming collection really contains only instances of E, but:
The static type of the incoming
collection might differ, perhaps
because the caller doesn’t know the
precise type of the collection being
passed in, or perhaps because it is a
Collection<S>,where S is a
subtype of E.
It’s perfectly
legitimate to call containsAll() with
a collection of a different type. The
routine should work, returning false."
Compatibility.
Before generics were available, there was just get(Object o).
Had they changed this method to get(<K> o) it would have potentially forced massive code maintenance onto java users just to make working code compile again.
They could have introduced an additional method, say get_checked(<K> o) and deprecate the old get() method so there was a gentler transition path. But for some reason, this was not done. (The situation we are in now is that you need to install tools like findBugs to check for type compatibility between the get() argument and the declared key type <K> of the map.)
The arguments relating to the semantics of .equals() are bogus, I think. (Technically they're correct, but I still think they're bogus. No designer in his right mind is ever going to make o1.equals(o2) true if o1 and o2 do not have any common superclass.)
The reason is that containment is determined by equals and hashCode which are methods on Object and both take an Object parameter. This was an early design flaw in Java's standard libraries. Coupled with limitations in Java's type system, it forces anything that relies on equals and hashCode to take Object.
The only way to have type-safe hash tables and equality in Java is to eschew Object.equals and Object.hashCode and use a generic substitute. Functional Java comes with type classes for just this purpose: Hash<A> and Equal<A>. A wrapper for HashMap<K, V> is provided that takes Hash<K> and Equal<K> in its constructor. This class's get and contains methods therefore take a generic argument of type K.
Example:
HashMap<String, Integer> h =
new HashMap<String, Integer>(Equal.stringEqual, Hash.stringHash);
h.add("one", 1);
h.get("one"); // All good
h.get(Integer.valueOf(1)); // Compiler error
There is one more weighty reason, it can not be done technically, because it brokes Map.
Java has polymorphic generic construction like <? extends SomeClass>. Marked such reference can point to type signed with <AnySubclassOfSomeClass>. But polymorphic generic makes that reference readonly. The compiler allows you to use generic types only as returning type of method (like simple getters), but blocks using of methods where generic type is argument (like ordinary setters).
It means if you write Map<? extends KeyType, ValueType>, the compiler does not allow you to call method get(<? extends KeyType>), and the map will be useless. The only solution is to make this method not generic: get(Object).
Backwards compatibility, I guess. Map (or HashMap) still needs to support get(Object).
I was looking at this and thinking why they did it this way. I don't think any of the existing answers explains why they couldn't just make the new generic interface accept only the proper type for the key. The actual reason is that even though they introduced generics they did NOT create a new interface. The Map interface is the same old non-generic Map it just serves as both generic and non-generic version. This way if you have a method that accepts non-generic Map you can pass it a Map<String, Customer> and it would still work. At the same time the contract for get accepts Object so the new interface should support this contract too.
In my opinion they should have added a new interface and implemented both on existing collection but they decided in favor of compatible interfaces even if it means worse design for the get method. Note that the collections themselves would be compatible with existing methods only the interfaces wouldn't.
We are doing big refactoring just now and we were missing this strongly typed get() to check that we did not missed some get() with old type.
But I found workaround/ugly trick for compilation time check: create Map interface with strongly typed get, containsKey, remove... and put it to java.util package of your project.
You will get compilation errors just for calling get(), ... with wrong types, everything others seems ok for compiler (at least inside eclipse kepler).
Do not forget to delete this interface after check of your build as this is not what you want in runtime.

Why does the key parameter in Map.get() is not generic? [duplicate]

What are the reasons behind the decision to not have a fully generic get method
in the interface of java.util.Map<K, V>.
To clarify the question, the signature of the method is
V get(Object key)
instead of
V get(K key)
and I'm wondering why (same thing for remove, containsKey, containsValue).
As mentioned by others, the reason why get(), etc. is not generic because the key of the entry you are retrieving does not have to be the same type as the object that you pass in to get(); the specification of the method only requires that they be equal. This follows from how the equals() method takes in an Object as parameter, not just the same type as the object.
Although it may be commonly true that many classes have equals() defined so that its objects can only be equal to objects of its own class, there are many places in Java where this is not the case. For example, the specification for List.equals() says that two List objects are equal if they are both Lists and have the same contents, even if they are different implementations of List. So coming back to the example in this question, according to the specification of the method is possible to have a Map<ArrayList, Something> and for me to call get() with a LinkedList as argument, and it should retrieve the key which is a list with the same contents. This would not be possible if get() were generic and restricted its argument type.
An awesome Java coder at Google, Kevin Bourrillion, wrote about exactly this issue in a blog post a while ago (admittedly in the context of Set instead of Map). The most relevant sentence:
Uniformly, methods of the Java
Collections Framework (and the Google
Collections Library too) never
restrict the types of their parameters
except when it's necessary to prevent
the collection from getting broken.
I'm not entirely sure I agree with it as a principle - .NET seems to be fine requiring the right key type, for example - but it's worth following the reasoning in the blog post. (Having mentioned .NET, it's worth explaining that part of the reason why it's not a problem in .NET is that there's the bigger problem in .NET of more limited variance...)
The contract is expressed thus:
More formally, if this map contains a
mapping from a key k to a value v such
that (key==null ? k==null :
key.equals(k)), then this method
returns v; otherwise it returns null.
(There can be at most one such
mapping.)
(my emphasis)
and as such, a successful key lookup depends on the input key's implementation of the equality method. That is not necessarily dependent on the class of k.
It's an application of Postel's Law, "be conservative in what you do, be liberal in what you accept from others."
Equality checks can be performed regardless of type; the equals method is defined on the Object class and accepts any Object as a parameter. So, it makes sense for key equivalence, and operations based on key equivalence, to accept any Object type.
When a map returns key values, it conserves as much type information as it can, by using the type parameter.
I think this section of Generics Tutorial explains the situation (my emphasis):
"You need to make certain that the generic API is not unduly restrictive; it must
continue to support the original contract of the API. Consider again some examples
from java.util.Collection. The pre-generic API looks like:
interface Collection {
public boolean containsAll(Collection c);
...
}
A naive attempt to generify it is:
interface Collection<E> {
public boolean containsAll(Collection<E> c);
...
}
While this is certainly type safe, it doesn’t live up to the API’s original contract.
The containsAll() method works with any kind of incoming collection. It will only
succeed if the incoming collection really contains only instances of E, but:
The static type of the incoming
collection might differ, perhaps
because the caller doesn’t know the
precise type of the collection being
passed in, or perhaps because it is a
Collection<S>,where S is a
subtype of E.
It’s perfectly
legitimate to call containsAll() with
a collection of a different type. The
routine should work, returning false."
Compatibility.
Before generics were available, there was just get(Object o).
Had they changed this method to get(<K> o) it would have potentially forced massive code maintenance onto java users just to make working code compile again.
They could have introduced an additional method, say get_checked(<K> o) and deprecate the old get() method so there was a gentler transition path. But for some reason, this was not done. (The situation we are in now is that you need to install tools like findBugs to check for type compatibility between the get() argument and the declared key type <K> of the map.)
The arguments relating to the semantics of .equals() are bogus, I think. (Technically they're correct, but I still think they're bogus. No designer in his right mind is ever going to make o1.equals(o2) true if o1 and o2 do not have any common superclass.)
The reason is that containment is determined by equals and hashCode which are methods on Object and both take an Object parameter. This was an early design flaw in Java's standard libraries. Coupled with limitations in Java's type system, it forces anything that relies on equals and hashCode to take Object.
The only way to have type-safe hash tables and equality in Java is to eschew Object.equals and Object.hashCode and use a generic substitute. Functional Java comes with type classes for just this purpose: Hash<A> and Equal<A>. A wrapper for HashMap<K, V> is provided that takes Hash<K> and Equal<K> in its constructor. This class's get and contains methods therefore take a generic argument of type K.
Example:
HashMap<String, Integer> h =
new HashMap<String, Integer>(Equal.stringEqual, Hash.stringHash);
h.add("one", 1);
h.get("one"); // All good
h.get(Integer.valueOf(1)); // Compiler error
There is one more weighty reason, it can not be done technically, because it brokes Map.
Java has polymorphic generic construction like <? extends SomeClass>. Marked such reference can point to type signed with <AnySubclassOfSomeClass>. But polymorphic generic makes that reference readonly. The compiler allows you to use generic types only as returning type of method (like simple getters), but blocks using of methods where generic type is argument (like ordinary setters).
It means if you write Map<? extends KeyType, ValueType>, the compiler does not allow you to call method get(<? extends KeyType>), and the map will be useless. The only solution is to make this method not generic: get(Object).
Backwards compatibility, I guess. Map (or HashMap) still needs to support get(Object).
I was looking at this and thinking why they did it this way. I don't think any of the existing answers explains why they couldn't just make the new generic interface accept only the proper type for the key. The actual reason is that even though they introduced generics they did NOT create a new interface. The Map interface is the same old non-generic Map it just serves as both generic and non-generic version. This way if you have a method that accepts non-generic Map you can pass it a Map<String, Customer> and it would still work. At the same time the contract for get accepts Object so the new interface should support this contract too.
In my opinion they should have added a new interface and implemented both on existing collection but they decided in favor of compatible interfaces even if it means worse design for the get method. Note that the collections themselves would be compatible with existing methods only the interfaces wouldn't.
We are doing big refactoring just now and we were missing this strongly typed get() to check that we did not missed some get() with old type.
But I found workaround/ugly trick for compilation time check: create Map interface with strongly typed get, containsKey, remove... and put it to java.util package of your project.
You will get compilation errors just for calling get(), ... with wrong types, everything others seems ok for compiler (at least inside eclipse kepler).
Do not forget to delete this interface after check of your build as this is not what you want in runtime.

Categories

Resources