I have a basic question about using the "Best Practices" in coding. (I'm using Java, but the question is general to OOP.) When writing method's for a class that are intended to be used in the long run, is it best to leave the return object with or without generics?
To be specific in my case, I'm returning a Map<String, Integer> with the method. Should I specify this in the return statement, or should I simply return a Map?
It is best to use generics whenever possible. It will help avoid runtime exceptions, and it won't force the people using your code to do a bunch of ugly type casting. For example, if you use the following signature:
Map<String, Integer> getMap();
... then the consuming code might look like this:
Map<String, Integer> map = getMap();
Integer val = map.get(key);
... but if you use a signature like this:
Map getMap();
... the consuming code might look like this:
Map<String, Integer> map = (Map<String, Integer)getMap();
Integer val = map.get(key);
By using generics, not only do you save that (Map<String, Integer>) cast, but in the event that you change getMap to actually return a Map<String, Object>, you will get a compile-time error (which is easy to catch and fix), rather than possibly getting an exception when you call map.get(key) and the JRE tries to do an automatic cast of some random Object into an Integer.
You should definitely return a Map<String, Integer> instead of a plain Map if it makes sense in your method, as this will make it easier for others to use said method - after getting the Map<String, Integer> they will be able to retrieve String keys and Integer values without having to cast them from a generic Object every time (this also makes it a little more typesafe as this way they will know what the keys and values are without even reading the javadoc for your method).
So in short, definitely, return generics.
If you are returning a collection, you should include the generic type that is contained by the collection. For example:
public Map<String, Blammo> getBlammoMap(...)
is (IMHO) preferred to
public Map getBlammoMap(...)
Because it
Limits the options of a bad cast (i.e. kapowMap = (Map<String, Kapow> getBlammoMap()).
Tells the consumer what the method is actually returning.
If the method is clearly intended to work with a certain type (i.e. only String), then it's ok to return a List<String>. If the method is generic taking a type parameter T, you can return List<T>.
I would not simply return a Map, because usually it causes confusion and more boiler-plate code to convert to the desired type.
In general, your type parameters, both input and output, should capture the level of specificity of the precise function. In functional programming, they go so far as to say "the types are the documentation." If I were to see Map foo(Arg args) I would think that foo is in no way concerned with the types in the Map it returns, but somehow relies on Args for something." If I were to see Map<T,String> foo(T t, Arg arg) or Map<T, U> foo(T t, U u) I would think "OK, foo produces a Map based on the type of its t and with a String produced by Arg (or by the U from u)."
In terms of preference, clearly you want to be as clear as possible to the future programmer (yourself or others). Just as returning Map without type-params is obscure, so too would returning Map<String, Integer> might be overly restrictive (and thus misleading) if your foo would work equally well with, say, Map<String, Long>
I believe that more specific, the better. If your method is return a map that always has Strings as the key, and Integers as the value, then definitely use the Map has the return type. That way, any calling code knows exactly what they're getting. If the return type was just Map, then the calling code would have no idea what the class the keys and values are (Other than Object).
In general, you should probably always specify paramerize Maps, Lists, etc., so it's known exactly what it contains. This is very helpful when iterating over them and you can use a java foreach.
for (String currKey : myMap.keySet())
{
System.out.println("curr Key: " + currKey + " curr Value: " + myMap.get(currKey));
}
This eliminates any extra iterators or casting.
Ho-ho-ho! A pretty New Year question.
You generally must (MUST) return a proper generic Map<Whatever, YouNeed>. It may sound crazy, but as soon as you use any generic type without type parameters, you're getting into trouble.
The trouble will be as follows: raw types, being used in the code, change the way methods (even seemingly non-related ones) are resolved. Find a presentation by Joshua Bloch and Bill Pugh called "Java Puzzlers: Scraping the Bottom of the Barrel" for details whle I'm preparing an example :) The video with details is at http://www.parleys.com/#id=2168&st=5 (you may want to scroll to slide 44, 5. "Glommer Pile")
So here's an example:
/**
* (c) (as far as I know) Joshua Bloch and Bill Pugh, 2010
*/
public class Glommer<T> {
String glom(Collection<?> objs) {
String result = "";
for (Object o : objs) result += o;
return result;
}
int glom(List<Integer> ints) {
int result = 0;
for (int i : ints) result += i;
return result;
}
public static void main(String args[]) {
List<String> strings = Arrays.asList("1", "2", "3");
System.out.println(new Glommer().glom(strings));
}
}
Question is, whether it
prints 6
prints 123
throws an exception,
or does something else.
Try to guess. Then compile (yes it compiles) and see what happens.
Now that does not apply to your case. But having a habit of always specifying the type, even if it will be just Map<?,?>, is extremely helpful. You won't lose.
The obligatory Java Generics FAQ link
Related
I have an Object (Object o) which when I call o.toString() it prints:
{data-ved=0EMIuCBcoAA, data-pid=23, href=https://mail.google.com/mail/?tab=wm, class=gb_P}
As you can see there seems to be an array of Strings in this object. Is there a way to directly convert the object to a String[]?
The expected result would be a String[] containing something like:
data-ved=0EMIuCBcoAA
data-pid=23
href=https://mail.google.com/mail/?tab=wm
class=gb_P
Currently, I'm using Regex to extract the parts of the String I need (as array).
Is there a better way to do this?
If it matters, the object is being returned from a JavaScript Executor in Selenium.
As can be seen in the comment trail the object that is returned seems to be a Map (and probably a Map<String, Object> or Map<String, String>). Thus you could do the following:
if( o instanceof Map ) {
Map map = (Map)o; //If you know the generic type you could use Map<String, Object> etc. instead
String dataVed = (String)map.get("data-ved");
...
}
As you can see casting the value is needed as well since there's no generic type provided for the map and thus all you know is that keys and values are objects of any class.
If you know what generic type should be used you could improve that, especially if the value type is String. Unfortunateley due to type erasure you can't get that type at runtime so the only way to get to know what to expect would be some documentation.
I am currently reading "Core Java for the Impatient" by Horstmann (I recommend it, love the concise style) and I have a trouble understanding one of the exercises pertaining to the collection API. The exercise is as follows:
I encouraged you to use interfaces instead of concrete data structures, for example, a
Map instead of a TreeMap. Unfortunately, that advice goes only so far. Why can’t
you use a Map<String, Set<Integer>> to represent a table of contents?
(Hint: How would you initialize it?) What type can you use instead?
The following code compiles and works without a problem though, even though the interfaces were used for variable declarations. What am I missing?
Map<String, Set<Integer>> toc = new HashMap<>();
toc.put("element1", IntStream.of(1, 2, 3).boxed().collect(Collectors.toSet()));
toc.put("element2", IntStream.of (3, 4, 7).boxed().collect(Collectors.toSet()));
toc.forEach( (k, v) -> {
System.out.print(k + " ");
v.forEach(val -> System.out.print(val + " "));
System.out.println();
} );
}
An interface like Map is a supertype for all interfaces that inherit it and all classes that implement it. So TreeMap inherits from Map, and because you can always assign to a variable any reference that is of a subtype, it's perfectly acceptable to assign a TreeMap reference to a Map variable. This is called a widening reference conversion
https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.5
"Widening reference conversions never require a special action at run time and therefore never throw an exception at run time. They consist simply in regarding a reference as having some other type in a manner that can be proved correct at compile time."
So, yes, you certainly can use a Map<String, Set<Integer>> to represent something in your domain model, but you can't instantiate an interface directly; you must instantiate a concrete type (class) that implements it. This is exactly what you did when you declared
Map<String, Set<Integer>> toc = new HashMap<>();
As an extension of this principle, you could just as easily write
AbstractMap<String, Set<Integer>> toc = new HashMap<>();
since AbstractMap is also a supertype of HashMap.
In general you want to declare the widest type for the variable that can hold the largest possible set of subtype references that work in your logic. If you need a sorted map, then 'Map' is too wide; it doesn't enforce sortedness. You'd have to declare the variable as TreeMap , or better, SortedMap for that.
Usually the interface is the widest applicable type, but you have to think about it in case it's not.
EDIT: Mentioned SortedMap in light of comment.
I have got in touch with book's author and he agreed that question was unclear and it was to lead the reader to use wildcard types. The exercise in question was changed to:
Suppose you have a method parameter of type Map<String, Set<Integer>>, and someone calls your method with a HashMap<String, HashSet<Integer>>. What happens? What parameter type can you use instead?
The answer is that in this case one should use the wildcard type: Map<String, ? extends Set<Integer>>.
I am converting old java code to support generics and came across this line of code which was trying to cast an object retrieved from the session into a TreeMap:
TreeMap allTransactions = (TreeMap) pageContext.getSession()
.getAttribute("allTransactions");
When I tried to convert it to specific type:
TreeMap<String, MyDataBean> allTransactions = (TreeMap<String, MyDataBean>)
pageContext.getSession().getAttribute("allTransactions");
It gave me a warning:
Type safety: Unchecked cast from Object to TreeMap<String,MyDataBean>
In an effort to get rid of the warning completely, I wrote a method to cast it to Map:
public static <K,V> Map<K,V> castToMap(Class<? extends K> clazz1,
Class<? extends V> clazz2, Map<?,?> c) {
Map<K,V> map = new TreeMap<K,V>();
for (Map.Entry<?,?> entry : c.entrySet()) {
Object key = entry.getKey();
Object value = entry.getValue();
map.put(clazz1.cast(key), clazz2.cast(value));
}
return map;
}
Goodnews: This time I did not get any error when I modified the initial code to call this method:
Map<String, MyDataBean> allTransactions = MyUtilityClass.castToMap(String.class,
MyDataBean.class,
(Map<?,?>)pageContext.getSession().getAttribute("allTransactions"));
But I still had to cast it ^^^here to call my function.
Question 1:
Why it does not show any errors now when I am still using the cast (Map<?,?>) versus what I tried before (TreeMap<String,MyDataBean>)?
Question 2:
My app is broken as of now since other developers have not committed the code yet, and so I am in no position to run the App and verify its correctness. Can anyone tell just by looking at it, if this casting will behave the same way as before?
If it is a Map then it is implicitly a Map<?,?> - as in, a map of anything. It considers that to be a safe cast because, since it is a map, it cannot fail. At this point, it's no different than casting it to any other object, which they're assuming is safe.
If it is a bad cast (say it's a List, not a Map) then it will explode there before anything else goes wrong. But because those types are lost at compile time, it will happily continue if the problem is in the generic types. So Map<?,?> is considered safe because it will explode because of being not being a Map if that's the problem.
As far as your second issue, it looks fine, but without more context it's hard to say. That being said, you should be able to use the history in your source control to grab an earlier version to compare against. If you can't obtain earlier versions of your code, your source control isn't very useful, now is it?
Ad 1.: The cast itself is safe, because you don't specify any types which the compiler can not match against the method signature. Java 4 List can be safely assigned to Java 5 List<?>, but when you specify a type, you assume something, the compile can not safely check (therefore the warning).
Ad 2.: You have substituted a "hard" language cast with a "soft" method call cast, which will fail equally, if the classes are not castable into one another - I prefer the less verbose version of language casting, because one should rely one one's API.
This question is in response to another question by opensas: building a generic initializer function in java
From his question it became clear that he needs to convert from any data type T1 to another type T2. When I say "data type" here, I mean types limited to those commonly used to represent raw data: Integer, String, Date, etc. For the purpose of this question we can consider primitives to be boxed.
I'm wondering if there is any API that supports conversion between types where both the input and output are generalized to a set of supported data types. I had a look at Apache Commons' beanutils.converters package, but there's a separate converter class for each known input. I'm looking for any functionality that implements something like the following signature:
static <IN, OUT> OUT convert(IN value, Class<OUT> targetType);
or else
static <IN, OUT> OUT convert(IN value, OUT defaultValue);
It really wouldn't be too hard to implement this kind of mapping oneself, either using a bunch of else if blocks pointing to the various Commons Converters, or else a Map<Class<?>, Converter> for the same purpose. But I'm wondering if this kind of functionality is supported somewhere.
Also, if this winds up being a duplicate I apologize. I tried finding similar questions and was surprised when I found none matching this situation.
EDIT: so an example of this code in action would be:
Integer i = GenericConverter.convert("123", Integer.class); //returns 123
Date d = GenericConverter.convert(1313381772316L, Date.class); //returns today's date
Boolean b = GenericConverter.convert(0, Boolean.class); //returns false
Long l = GenericConverter.convert("asdf", Long.class); //RuntimeException
UPDATE: The BalusC code I linked falls close to the mark, and Bohemian's answer is a nice lightweight solution (although it doesn't work for Boolean conversions). He's also right that Dates should be probably be handled separately if we want to generalize conversion of these other data types. I'm still hoping for additional answers though - especially if there is more of a hands-off API available somewhere.
I am not aware of any library, however the code is just one line.
Apart from Date, all boxed primitives have a String construtor, so this method does the trick:
public static <I, O> O convert(I input, Class<O> outputClass) throws Exception {
return input == null ? null : outputClass.getConstructor(String.class).newInstance(input.toString());
}
To cater for Dates, you could use instanceof within the method, but I would recommend a separate method, since converting dates is a format- and context-sensitive thing (eg String-->Date parses and uses which format?, Long-->Date sets the time).
I have deliberately left error/special handling to the reader as an exercise.
In JDK 8 this can be easily implemented with the new java.util.functions.Mapper interface and a lambda expression.
Mapper<String,Integer> atoi = s -> Integer.valueOf(s);
Integer r = atoi.map("10");
Using method references it can be even simpler:
Mapper<String, Integer> atoi = Integer::new;
Integer r = atoi.map("10");
Or things like:
List<Long> dates = asList(1344754620310L,1344754854877L);
List<Date> asDates = dates.map(Date::new).into(new ArrayList<Date>());
Or cool conversions like:
List<Integer> myInts = "5,4,3,2,1,0,6,7,8,9"
.splitAsStream(",")
.map(Integer::new)
.into(new ArrayList<Integer>());
In the current implementation of the JDK8 API, a few default mappers have been defined (i.e. LongMapper, IntMapper, DoubleMapper) and there's a utility class called Mappers that defines some others like a string mapper, and identity mapper, a constant mapper, etc.
I am not sure if this is what you are after, but certainly it must be a nice way to implement it.
Cases like the one you suggest for:
static <IN, OUT> OUT convert(IN value, Class<OUT> targetType);
Can be implemented with the Mappers utility class:
Mapper<String, Integer> atoi = Mappers.instantiate(String.class, Integer.class);
Integer r = atoi.map("10");
And your signature:
static <IN, OUT> OUT convert(IN value, OUT default);
Could be implemented with something like:
Mapper<String, Integer> atoi = chain(substitute(null, "0"), Integer::new);
Integer r = atoi.map(null); //produces 0
As such, a code like this...
List<String> data = asList("0", null, "2", null, "4", null, "6");
List<Integer> myInts = data.map(chain(substitute(null, "0"), Integer::new)).into(new ArrayList<Integer>());
System.out.println(myInts);
Would yield: [0, 0, 2, 0, 4, 0, 6]
If you are using Spring Framework (spring-core), you can use class
org.springframework.core.convert.support.DefaultConversionService
Default constructor adds many types converter and you can add your own by implementing Converter interface and call addConverter(Converter). There is also nice unit test showing some conversion combinations.
I found something by BalusC that looks close to what I'm asking for: http://balusc.blogspot.com/2007/08/generic-object-converter.html
Unfortunately nothing involving Date conversion is supported, but as the comments indicate, more conversion methods are easily added. His class is essentially a nice little framework that uses reflection to gather up all conversion methods at runtime and put them in a HashMap<String, Method> where the key String is a unique id for that input-output combination.
Still looking for other suggestions! Particularly for an API that would be more hands off than this code I linked to.
Take a look at Variance, which allows you to set up a type conversion context with various converters registered and then move values into and out of a Variant type with type conversion handled by the context.
Variant aVariant = Variant.of("1.2345");
double aDouble = aVariant.doubleValue();
int anInt = Variant.of("12").intValue();
String aString = Variant.of(12.0).toString();
Date aDate = Variant.of("2012-04-06").as(Date.class);
String anIsoFormattedDate = Variant.of(aDate).in(isoDateFormattingContext).toString()
Converters are just Guava Functions from one type to another, and you can register your own, overriding existing conversions where desired.
Edit: I've gotten a couple of answers that say what I already said in the question. What I am really interested in is finding corroborating reference material.
I am looking at a code sample that more or less follows this pattern:
Map<String, List> getListsFromTheDB() {
Map<String, List> lists = new HashMap<String, List>();
//each list contains a different type of object
lists.put("xList", queryForListOfXItems());
lists.put("yList", queryForListOfYItems());
return lists;
}
void updateLists() {
Map<String, List> lists = getListsFromTheDB();
doSomethingWith(lists.get("xList"));
doSomethingWith(lists.get("yList"));
}
My feeling is that this is an anti-pattern. What the coder should have done is create a class which can be returned, like this:
class Result {
private final List<X> xList;
private final List<Y> yList;
public Result(xList, yList) {
this.xList = xList;
this.yList = yList;
}
public List<X> getXList() { xList; }
public List<Y> getYList() { return yList; }
}
This would be more type-safe, avoid over-generalizing a very specific problem, and be less prone to errors at runtime.
Can anyone point me to any authoritative reference material which specifies that you should avoid this kind of pattern? Or, alternately, if it's actually a good pattern, please give justification.
I think the point is the number of Lists is fixed. Since you ensure the code uses 2 lists, the map is a little bit over-generalizing.
So 'class Result' is better I think.
I say it depends on the context.
If you return a map, the caller has to know the 'magic' keys "xList" and "yList" to get the actual data out of the map. I mean magic as magic constants. (You could iterate over the map to find the magic keys, but that's just a trick.) By using the map you have actually hidden the data, making it harder to get what one wants (x- and yLists).
The magic constants do not have to be so magically. If "xList" and "yList" would be the table names in the database (or whatever external strings), then I would expect to get a mapping from table names to object lists. Someone might add/rename/delete tables. (Or, maybe prettier, I would like to be able to query per table like getListFromTheDB("xList");. )
In your code you got this method
queryForListOfXItems();
That does smell like hard coded xList and yList. That would thus (IMO) make a map a bad choice.
I agree with you.
Obviously the guy was lazy and used a Map to avoid creating a new class.
The side effect is that the code that need to use getListsFromTheDB() will be less readable, and, as you mentionned, more error-prone.
Of course, there is the alternative in which the caller creates the lists:
void fillFromTheDB(List<X> xList, List<Y> yList) {
//each list contains a different type of object
xList.addAll(queryForListOfXItems());
yList.addAll(queryForListOfYItems());
}
void updateLists() {
List<X> xList = new ArrayList<X>();
List<Y> yList = new ArrayList<Y>();
fillFromTheDB(xList, yList);
doSomethingWith(xList);
doSomethingWith(yList);
}
I don't have any authoritative material, but my gut feeling is that, unless there's something more complex going on in the real code, using a Map this way doesn't bother me. The Result class feels a bit like overkill, actually.