I'm writing a small API to deal with objects that have specific 'traits' In this case, they all have an interval of time and a couple of other bits of data, So I write an interface TimeInterval with some getters and setters.
Now most of these API methods deal with a Set or List of Objects. Internally these methods use the Java Colletions Framework (HashMap/TreeMap in particular). So these API methods are like:
getSomeDataAboutIntervals(List<TimeInterval> intervalObjects);
Couple of Questions:
a) Should this be List<? extends TimeInterval> intervalObjects instead?
Is it mostly a matter of style? The one disadvantage of taking strictly an interface that I can see is, you need to create your list as a List<TimeInterval> rather than List<ObjectThatImplementsTimeInterval>.
This means potentially having to copy a List<Object..> to List<TimeInterval> to pass it to the API.
Are there other pros & cons to either approach?
b) And, one dumb question :) The collections framework guarantees I always get out the same instance I put in, the collections are really a collection of references, correct?
1) Yes.
Method parameters should be as general as possible. List<? extends A> is more general than List<A>, and can be used when you don't need to add things to the list.
If you were only adding to the list (and not reading from it), the most general signature would probably be List<? super A>
Conversely, method return types should be as specific as possible. You rarely to never want to return a wildcard generic from a method.
Sometimes this can lead to generic signatures:
<T extends MyObject> List<T> filterMyObjects(List<T>)
This signature is both as specific and as general as possible
2) Yes, except possibly in some rare very specific cases (I'm thinking of BitSet, although that isn't technically a Collection).
If you declare your list as List<? extends A>, then you can pass in any object which static type is List<X>, where X extends A if A is a class, or X implements A id A is an interface. But you'll not be able to pass in a List or a List<Object> to it (unless A is Object) without force-casting it.
However, if you declare the parameter as a List<A>, you'll only be able to pass lists which static type is strictly equivalent to List<A>, so not List<X> for instance. And by "you are not able to do otherwise", I really mean "unless you force the compiler to shut up and accept it", which I believe one should not do unless dealing with legacy code.
Collections are really collections of references. The abstraction actually is that everything you can put in a variable is a reference to something, unless that variable is of a primitive type.
1) I would recommend ? extends TimeInterval. Because of Java's polymorphism, it may not actually make a difference, but it is more robust and better style
2) Yes
a) No. List<? extends TimeInterval> will only accept interfaces that extend the interface TimeInterval. Your assertion that "you need to create your list as a List<TimeInterval> is wrong, unless I misunderstand your point. Here's an example:
List<List> mylist= new ArrayList<List>();
mylist.add(new ArrayList());
b) Yes.
Should this be List intervalObjects instead?
You only do that if you want to pass in a List<TimeIntervalSubclass>. Note you can put instances of subclasses of TimeInterval into a List<TimeInterval>. Keep in mind that the type of the list is different than the types in the list.
If you do List<? extends A> myList -- that only affects what you can assign to myList, which is different than what is in myList.
And, one dumb question :) The collections framework guarantees I
always get out the same instance I put in, the collections are really
a collection of references, correct?
When you create a collection Map myMap = new HashMap(), myMap is a reference to the underlying collection. Similarly, when you put something into a collection, you are putting the reference to the underlying object into the collection.
Related
I know that Stream.concat exists (doc) to concatenate two streams. However, I have run into cases where I need to add "a few more" items to an existing stream, and then continue processing on it. In such a situation, I would have expected to be able to chain together methods like:
getStream(someArg)
.map(Arg::getFoo)
.concat(someOtherStreamOfFoos) // Or append, or...
.map(...)
However, no such instance-level chainable append/concat method exists.
This isn't a question asking for solutions to this problem, or more elegant approaches (although I would of course be grateful for any other viewpoints!). Rather, I'm asking about the design factors that led to this decision. The Stream interface was, I trust, designed by some extremely smart people who are aware of the Principle of Least Astonishment - so, I must assume that their decision to omit this (to me) intuitively-obvious method signifies either that the method is an antipattern, or that it is not possible due to some technical limitation. I'd love to know the reason.
I can give you one reason it wouldn't have worked.
Stream.concat is defined as
static <T> Stream<T> concat(Stream<? extends T> a,
Stream<? extends T> b)
You can concat a Stream<HashMap> and Stream<Map> into a Stream<Map>, or even concat a Stream<HashMap> and a Stream<TreeMap> into a Stream<Map>. To do that with an instance method, you would need to be able to declare a type parameter like <U super T>, which Java doesn't allow.
// It'd look kind of like this, if Java allowed it.
public <U super T> Stream<U> concat(Stream<? extends U> other)
Java only allows upper-bounded type parameters, not lower-bounded.
Concatenating a Stream<Something> and a Stream<SomethingElse> might seem unusual, but type inference often produces type parameters too specific to work with an instance method. For example,
Stream.concat(Stream.of(dog), animalStream)
which would require an explicit type parameter if written as
Stream.<Animal>of(dog).concat(animalStream)
I think it is just missed functionality of Stream API.
Note that RxJava's Observable has method "concatWith" with required functionality, so your question is reasonable:
Observable<String> o1 = ...;
Observable<Integer> o2 = ...;
o1.map(s -> s.length())
.concatWith(o2)
....
Java 8 has another functionality is nice to have is get another Optional if current Optional is empty, like:
Optional.ofNullable(x).orElse(anotherOptional)
What I want to say that this concat you described is possible, just not implemented in the Stream.
I have seen a lot of cases where in, the ? type is used only to create references. And we are not able to add any Objects other than null. So, is the use of '?', only to create references?
Also, what is the need / use of giving the option to add a null? They could simply have implemented with no add() method at all, right? Any reason for giving the option of adding null into List < ? > ?
List is a generic interface, and the same interface regardless of the generic type. As such, all implementors are required to redefine all methods in it. As far as being able to add null into an "unbounded" list, it's not as much a matter of methods as much as a requisiste of respecting the interface:
the unbound generic wildcard allows a reference to a List (or other generic class) to be passed around to methods without requiring a compile-time knowledge of the specific type it will contain. However, the contract of the generic must still be respected. This poses no big problem for read operations, since whatever is in the List, it will always (also) be a java.lang.Object. On the other hand, insert operations are almost impossible, since lacking informations about the accepted types, no type can be accepted - e.g. a List can only accept strings, but an Object could be anything else - a Number for example, and thus cannot be accepted safely.
Null works safely since (assuming the List accept null values), it will be the only value that's always valid to be put inside the list (a null can be cast to any valid type).
Edit: just since i noticed i didn't actually answer your question: yes, the <?> wildcard (and the extended forms as well <? extends X> and <? super X> are only valid when defining references, and cannot be used in generic instantiation.
Firstly, null is a member of all types. That's part of the language specification that pre-dates generics, so you can' do anything about that.
As for the use of ?, you can't * instantiate* a wildcard-typed generic class, that you can't do this:
List<? extends Number> list = new ArrayList<? extends Number>(); // error
because it does't make sense. The wildcard type is used to tell the compiler "I don't know what the type of the list will be, but it will be bounded by Number". However, when the code executes, a List with a type bounded by Number will be assigned to the variable.
Wildcards are most often seen as parameters:
public static void doSeomthing(List<? extends Number> list) {
// use the list knowing only that the elements are a subclass of Number
// but knowing which exact class they are
}
I recently had an exam on Java, and there was a wide section about wildcard generics in Java. However, there is very little said about their usage in practice. When should we use them? Let's see a typical usage:
void check(Collection<? extends Animal> list) {
// Do something
}
What the documentation says, that this collection does not allow to add any elements to the list. So basically wildcards can be used for making collections read-only. Is that their only usage? Is there any practical need for that? For the last four years I took part in a lot of programming projects in Java, but I haven't seen any project that would use extensively such a feature as wildcard.
So, from the practical point of view, are there any situations when wildcard generics are unavoidable and necessary?
So, from the practical point of view, are there any situations when
wildcard generics are unavoidable and necessary?
I don't think they are 'unavoidable and necessary' because the Java compiler erases them anyway. However, when using them you get the benefit of a tighter type check during compile-time and you avoid type casting. Who wants to type cast anyway? :)
Guidelines for Wildcard Use
Type Erasure
void check(Collection<? extends Animal> list) {
list.add(new Animal()); // compile time error
list.add(new Dog()); // compile time error. Dog is subclass of Animal class.
}
Java has develop such generics because to disallow the programmar to code whatever they want otherwise if it is allowed then later they will find a mess in run-time environment.
Sometime in programming you will get a scenario where your method check would not wan't to add element in the list but want to read those element.
You can only add null values.
In brief, what you are saying is wrong.
It has nothing to do to "make collections read-only".
We can still add elements to the input list, because Collection did declare a add(E) method.
The wildcard is straight-forward I think: You actually want to constraint the input type, because your logic is only reasonable for certain type.
For your example, your check maybe using some method of Animal
void check(Collection<? extends Animal> list) {
// Do something
for (Animal a : list) {
a.checkAge(); // checkAge() is method of Animal
}
}
Without ? extends Animal, the above code will not work, as the incoming list can be collection of anything (not Animal).
Therefore, we want to make sure the incoming collection to Collection or Collection etc, so that our code actually make sense as we are retrieving elements from the list and treated the element as Animal.
This is an Android app, but presumably it happens the same in Java. I have a type LevelFactory from which I derive Level1Factory, Level2Factory, etc. I want to have an array of these classes so I can instantiate a level given the array index.
I can just have a Class[] and put them in that and then just cast them to LevelFactory when I need to use them, but I was wondering what the proper thing to do is.
This is obviously an error "Incompatible types":
new Class<LevelFactory>[] {Level1Factory.class,Level2Factory.class};
However, I was surprised to see that this is also an error "Generic array creation":
new Class<? extends LevelFactory>[] {Level1Factory.class,Level2Factory.class};
The following works, but it gives the "Unchecked assignment" warning when assigned to a variable:
new Class[] {Level1Factory.class,Level2Factory.class};
The last is the only option I can get to work. I just ignore the warning, but I would like to do it using generics if that's actually possible.
I would recommend you to read Item 25 "Prefer lists to arrays" of book "Effective Java". There Joshua Bloch writes:
Why is it illegal to create a generic array? Because it isn’t typesafe. If it were
legal, casts generated by the compiler in an otherwise correct program could fail at
runtime with a ClassCastException. This would violate the fundamental guarantee provided by the generic type system.
UPD: Maybe with concrete example it would be more understandable.
First of all arrays are covariant which means that SuperClass[] can be cast to SubClass[] and vice versa. It also means that it's legal to cast AnyConcreteClass[] to, say, Object[].
So Let's assume that it's possible to have Set<Cat>[] (but it is NOT). If somebody cast this array to Object[] and then add there a set of Dog instances, Java couldn't guarantee anymore that our array contains only sets of Cat instances. Breaking type safety it breaks essence of generics. That is why it's illegal have generic array.
Set<Cat>[] cats = new Set<Cat>[]; // illegal
Object[] objects = cats;
objects[1] = new Set<Dog>();
cats[1].add(new Cat()); // Oops! TypeCastException
Honestly saying this example also was taken from Effective Java :)
Two questions:
Do you really need an Array? Arrays don't work great with generics. So an ArrayList<LevelFactory> might be the better solution
Do you really need the downcast to the special type (Level1Factory, Level2Factory)? If they have a common super method which is defined in LevelFactory (lets say Level getLevel()) you should not need to downcast them. Just call getLevel() and you get the correct Level instance from your factory.
Another note because this seems to be a common pitfall:
new Class<? extends LevelFactory>
This is not a valid statement (it does not matter if its an array or not). <? extends T> is only valid for the type on the left side. It defines that the generic type of the created Object can be T or derived from T. For Collections this does not mean that they can store objects of T or derived from T (which can a Collections of T anyway).
List<LevelFactory> list = new ArrayList<LevelFactory>() This means you can add objects of LevelFactory, Level1Factory and Level2Factory to list. When you want to receive objects from list they are of type LevelFactory.
List<? extends LevelFactory> list = new ArrayList<LevelFactory>() Means you can receive objects of LevelFactory from list. BUT you cannot add any object to list in a typesafe way because you don't know the exact generic type of list. That because you can also assign new ArrayList<Level1Factory>() to list. Which means that you can't even add LevelFactory objects to list because they don't implement Level1Factory.
In general <? extends Something> on collections is not what you want in most cases.
You can't create array this way new SomeClass<Type>[10] but only this way new SomeClass[10]. Consider using ArrayList<SomeClass<Type>> instead.
I have this method which unique parameter (List elements) sets elements to a ListModel, but I need to make a validation to see if the generic type implements comparable and since such thing like:
if (elements instanceof List<? extends Comparable>)
is illegal, I don't know how to do the proper validation.
Update
I already have done this validation using:
(elements.size() > 0 && elements.get(0) instanceof Comparable)
but I want to know if is there a cleaner solution for that, using for example reflection?
Thanks in advance.
The generic type of a list is erased at runtime. For this to work you need to either require the parameter in the method signature or test each element individually.
public void doSomething(List<? extends Comparable> elements) {}
OR
for (Object o : elements) {
if (o instanceof Comparable) {}
}
If you have control over the code, the former is preferred and cleaner; the later can be wrapped in a utility method call if needed.
That's not possible. instanceof is a runtime operator whereas generic information is lost at runtime (type-erasure).
I'm not a Generics guru, but to my understanding the reason you can't do that is that at runtime, there's no distinction between an ArrayList and, say, an ArrayList<String>. So it's too late to perform the test you want.
Why not just declare your method to accept a List<? extends Comparable> instead of a List?
In particular, the way you phrased your question makes it sound like you expect the list to always contain homogeneous elements, but a plain old List doesn't give you any sort of assurance like that.
To your update: simply making sure that one element implements Comparable is not enough (what if the other ones don't?) And making sure that all of the elements implement Comparable is also not enough to validate (what if they are of different classes that implement Comparable but cannot compare with each other?).
But the bigger question is, why bother validating at runtime anyway? What benefit does that give compared to simply trying to use it and then seeing that it fails (i.e. throws an Exception, which maybe you can catch)?