How to reuse a generic class without getting a compiler warning - java

Let's say I have the following domain model, which I simplified a little bit just for illustration purposes.
public abstract class JsonSerializer<T> {
public abstract JsonElement toJsonElement(final T object, final Locale locale);
public JsonArray toJsonArray(final Collection<T> objects, final Locale locale) {
return objects.stream().map(t -> toJsonElement(t, locale)).collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
}
}
public class FruitJsonSerializer<T extends Fruit> implements JsonSerializer<T> {}
public abstract class Fruit {}
public class Banana extends Fruit {}
public class Apple extends Fruit {}
Then, when I want to serialize any fruit, I do:
FruitJsonSerializer serializer = new FruitJsonSerializer();
serializer.toJsonElement(new Banana());
serializer.toJsonElement(new Apple());
But I get a warning from the compiler saying that it is an unchecked call to "toJsonElement" as a member of raw type.
How can I avoid this warning without having one serializer declaration per implementation (FruitJsonSerializer<Apple>, FruitJsonSerializer<Banana>, etc)?

Generic parameter should be a concrete class, not a subset of classes. You can declare JsonSerializer<Apple> or JsonSerializer<Banana>. Without a parameter it considered as JsonSerializer<Fruit>.
If you do not want to make FruitJsonSerializer generic then shouldn't write FruitJsonSerializer<T extends Fruit> if. It is enough to inherit it from JsonSerializer<Fruit>. That's my variant:
public static interface JsonSerializer<T> {
JsonElement toJsonElement(final T object);
JsonArray toJsonArray(final Collection<? extends T> objects, Locale locale);
}
public static class FruitJsonSerializer implements JsonSerializer<Fruit> {}
Note that wildcard used for generic parameter of Collection<? extends T>.

You can use the ? wildcard
FruitJsonSerializer<? super Fruit> serializer = new FruitJsonSerializer<>();
Edit: changed extends to super.

Use generics for a class A if
class A can handle multiple different object types with a base class or interface B,
you want to limit the usability of some A instances to specific B subclasses.
E.g. List can handle all Object subclasses, and a List<String> gets limited to String elements by compiler type checking and thus saves you from some types of subtle bugs.
Your JsonSerializer is a valid example calling for generics. But I have my doubts about FruitJsonSerializer.
I suggest to remove the type parameter from FruitJsonSerializer:
public class FruitJsonSerializer implements JsonSerializer<Fruit> {}
Your examples imply that you plan to use the very same FruitJsonSerializer for any kind of Fruit, so there seems to be no need for a FruitJsonSerializer <Apple> or any other specialization.
Maybe I'm wrong, then leave the class definitions as they are, and construct your instances the correct way:
FruitJsonSerializer<Fruit> generalSerializer = new FruitJsonSerializer<>();
FruitJsonSerializer<Apple> applesSerializer = new FruitJsonSerializer<>();
The first one can be used for any Fruit, the second only for Apples.
One more remark: change the toJsonArray() method to read:
JsonArray toJsonArray(final Collection<? extends T> objects, final Locale);
Otherwise you won't be able to pass a List<Apple> to a JsonSerializer<Fruit>.

Related

Java syntax: public class Me extends Thing<String,Integer,Character>

Disclaimer: I'm new to Java generics and collections.
Background: I've studied the basics of Java Generics here and here. Now I'm trying to understand how they apply to Hadoop's Mapper (public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable>)
Problem: Until today, I had only seen placeholders in the class definition (public class OrderedPair<K,V> implements Pair<K,V>), not concrete classes (public class Me extends Thing<String,Integer,Character>).
Question: So in general, if I have this...
public class Me extends Thing<String,Integer,Character>
...what does "extends Thing<String,Integer,Character>" mean? It seems that I'm "extending" Thing--that is, the Me subclass inherits the methods of the Thing superclass. Is that inheritance different from Thing<String,String,String>?
Clarification: Put another way, what is the difference between extending a class without generics (e.g., public class X extends Y) and with generics (public class X extends Y<a,b,c>)?
A generic class can have different types that it is used with (you choose which type to use it with when you instantiate an object of it). If you extend a generic class and do put concrete types there, as you did here: public class Me extends Thing<String,Integer,Character> that means that Me is extending Thing, but Thing is not generic anymore since it is now bound to the given types.
Previously you could instantiate Thing so:
Thing<String, Character, Integer> myThing = new Thing<>();
but Me is bound now, you can not choose types for it anymore.
Me myMe = new Me();
You could also extend Thing with actual generic types, so your Me class would be still generic.
public class <T, K, V> Me extends Thing<T, K, V>
...
// init with
Me<String, Character, Integer> myMe = new Me<>();
With this you can initialize Me anywhere with given generic types, that will be passed to the generic Thing.
You can also do it partially. So some types of Thing are fixed, and some will be free to choose at the time you instantiate Me.
public class <T> Me extends Thing<String, T, String>
...
// init with
Me<String> myMe = new Me<>();
When extending a class that has a generic type, you can choose to specify the type under which the subclass will be working. For example, class MyList extends ArrayList<String> will have the ArrayList methods, but will be specific to Strings.
In the same manner, when you extend Mapper, you need to specify what types of parameters the mapper will be working on. You do that by providing the type parameters Mapper declares.
Clarification: Put another way, what is the difference between
extending a class without generics (e.g., public class X extends Y)
and with generics (public class X extends Y<a,b,c>)?
The difference is that in the second case (generic class), you have to conform to the constraints of the types specified by the generic class if you want to compile fine or else you should declare a raw subclass.
Generally, the types specified by a generic class are used by the method of it.
So defining them correctly matters.
You have mainly 3 cases.
Takes this generic class declaration that specifies 3 parameters and that uses them in a myMethod() method :
public class Y <A extends AClass,B extends BClass, C extends CClass> {
public void myMethod(A a, B b, C c){
...
}
}
1) Your subclass is a raw class :
public class Z extends Y {
....
}
In this case, it compiles fine but with warning.
You lose benefits of generic in method invocations.
The compiler will consider the method of Z with this signature :
public void myMethod(Object a, Object b, Object c){
...
}
2) You subclass is a generic compliant subclass :
public class Z extends Y<ASubClass,BSubClass,CSubClass> {
....
}
The compiler will consider the method of Z with this signature :
public void myMethod(ASubClass a, BSubClass b, CSubClass c){
...
}
3) Your subclass is a generic class but not compliant with the parameters specified by the parent class, you have a compilation error.
public class Z extends Y<BSubClass,ASubClass,CSubClass> {
....
}
If the supertype uses generics, then, as for any generic type, referring to it without generic parameters is the bad mistake of using raw types. You must either use a generic type variable or concrete type for each type variable in the generic type. For example, a variable would be like
Thing<String, Integer, Character> thing = new Thing<>();
Naturally that is different from
Thing<String, String, String> thing = ...
because it handles different types!
Same thing holds for inheritance. In your case you're locking down the type parameters to concrete types.
public class SomeThing extends Thing<String, Integer, Character> { ...
locks down the types that SomeThing can handle, so that SomeThing is actually not a generic class.

Intertwined java generic interfaces and classes

I have a very specific problem with java generics. The follwowing classes and interfaces have been predefined:
public interface IFirst<R, T> {...}
public abstract class AbstractFirst<T extends AbstractFirst, L extends IFirst<String, T>> {...}
public interface ISecond extends IFirst<String, AbstractSecond> {...}
public abstract class AbstractSecond extends AbstractFirst<AbstractSecond, ISecond> {...}
Now I've created a following repo definition which seems to be valid:
public abstract class AbstractRepo<T extends AbstractFirst<T, IFirst<String,T>>> {...}
But now that i want to extend it:
public class RepoFirst extends AbstractRepo<AbstractSecond> {...}
I get the following error:
Bound mismatch: The type AbstractSecond is not a valid substitute for the bounded parameter
<T extends AbstractFirst<T,IFirst<String,T>>> of the type AbstractRepo<T>
I cannot change the first four (at least not radically) beacuse they are too heavily ingrained with the rest of the application, but the second two are new and up for change if need be.
Also intrestingly it allows the following (with raw type warnings):
public class RepoFirst extends AbstractRepo {
...
#Override
AbstractFirst someAbstractMethod() {
return new AbstractSecond() {...};
}
...
}
But for code clarity I would like to implement it with clearly defining AbstractSecond as the generic type for Abstract Repo.
What am I missing?
Your AbstractRepo expects an instance of IFirst and not a subtype of IFirst. But your AbstractSecond is clearly not IFirst. (I mean it is, from a OO standpoint but for generics, List<Number> is not the same as List<Integer>). It's ISecond. It might work if you could change your AbstractRepo from IFirst to ? extends IFirst as you did for AbstractFirst.

Java Generic of Another generic

I have interface:
interface Identifable<T extends Serializable> {
T getID();
}
and class that implement this:
public class Cat implements Identifable<Long> {
public Long getID(){...};
}
everything works fine. so far. Now I want to create GenericDAO, why I cannot create this?:
public abstract GenericDAO<T extends Identifable<S>> {
T getByID(S id);
}
I can only declare my GenericDAO as this:
public abstract GenericDAO<T extends Identifable, S> {
T getById(S id);
}
And complete class:
public CatDAO extends GenericDAO<Cat, Long> {
Cat getById(Long id);
}
But i think it's useless, because I repeat information. I already declared, that Cat implements Identifable< Long >, so why do I must declare GenericDAO< Cat, Long >, not just GenericDAO< Cat > ?
In Java, every generic type must be specified. You can go without specifying any type, but you can't go without specifying just one.
Also, every generic type must be specified in the declaration. If you want to have class GenericDAO<T extends Identifable<U>>, you must add the generic type declaration for U to your class declaration like this (since U is actually a generic type here):
public abstract class GenericDAO<T extends Identifable<U>, U>
The following is partially off-topic, but you might find it useful.
I've noticed that in your definition of GenericDAO two generic types are not tied to each other. This might not be what you want.
What you have here is a particular case in which the two generics are matching (the Long type in the Cat and CatDAO definitions). Consider having these declarations:
public class Dog implements Identifable<Long>
public class DogDAO extends GenericDao<Dog, String>
This would force you to write the getById method in DogDAO method:
Dog getById(String id);
Your getId method in the Dog returns a Long so your getById method int DogDAO would have to compare Strings to Longs. This is valid thing to do, but it's a bit counter-intuitive. Having a getById method for DogDAO that takes a Long parameter makes more sense, since the Dogs IDs are actually Longs.
If you want to tie the two types together, you can define the GenericDAO class as:
public abstract class GenericDAO<T extends Identifable<S>, S>
You still have to specify the second parameter, but at least the compiler can help you make sure that the types are matching.
Try this:
public abstract class GenericDAO<S extends Serializable, T extends Identifable<S>> {
abstract T getByID(S id);
}

An interface has two type parameters. Can I implement the interface with the two types being the same, such that they are then compatible?

This is an existing interface:
public interface MyInterface<T, U> {
public T foo(U u);
}
I want to implement this interface under the assumption that T and U are the same type. I thought maybe I could leave the type parameters in as they are, and then as long as I only ever instantiate this particular implementation with two of the same type, that it might work:
public class MyOuterClass<A> {
public class MyClass<T, U> implements MyInterface<T, U> {
#Override
public T foo(U u) {
return u; //error here
}
//even though in the only instantiation of MyClass, T and U are the same
private MyClass<A, A> myInstance = new MyClass<A, A>();
}
But, perhaps unsurprisingly, this doesn't work, as types T and U are incompatible.
So then I thought maybe I could change MyClass to specify that its types would always be the same, by changing it to something like MyClass<A, A> implements MyInterface<A, A> or similar, but I get errors saying that T is already defined.
Is there a way to implement MyClass so that its two types will be the same?
(I'm more of a C++ guy than Java, so sorry if I'm missing something fundamental about Java's generic's here.)
Your myclass needs to look like this:
public class MyClass<T> implements MyInterface<T, T> {
#Override
public T foo(T in) {
return in;
}
}
Let's review what your suggested class definition does:
public class MyClass<T, U> implements MyInterface<T, U>
In this code, T and U do two things each:
in the first occurance they define a type variable of your MyClass class
in the second occurance they specify the concrete type of the MyInterface class
Since inside the body of your class T and U are unbounded type variables (i.e. nothing is known about the actual types), they are assumed to be incompatible.
By having only a single type variable in your MyClass you make your assumption explicit: there's only a single type, and I'm using it for both types of the interface.
Last but not least: remember that the compilation of a type is complete once the source is fully handled. In other words: contrary to what C++ does, "instantiation" of a generic type ("template type" or similar in C++; Sorry for my rusty terminology) does not handle. MyClass<Foo> and MyClass<Bar> are the same type, as far as the JVM is concerned (only the compiler actually distinguishes them).
Define a single type parameter for MyClass:
class MyOuterClass<A> {
public class MyClass<T> implements MyInterface<T, T> {
public T foo(T u) {
return u;
}
}
// Need only one 'A' here.
private MyClass<A> myInstance = new MyClass<A>();
}
When you say
public class MyClass<T> implements MyInterface<T, T> {
... you are defining one generic variable for MyClass and you are saying that it fulfills both the roles T and U in MyInterface.

Java compile error with generics

public class IRock
{
public List<IMineral> getMinerals();
}
public class IMineral { ... }
public class SedimentaryMineral implements IMineral { ... }
public class SedimentaryRock implements IRock
{
private List<SedimentaryMineral> minerals;
#Override
public List<SedimentaryMineral> getMinerals()
{
return minerals;
}
}
Getting a compiler error:
Type mismatch: cannot convert from List<SedimentaryMineral> to List<IMineral>.
I understand that I can't convert an impl back to its API interface (because an API is just than - an API). But I'm confused as to why I'm getting a compiler error! Shouldn't Java honor the fact that SedimentaryMineral is an impl of IMineral and allow this?!?
Along with an explanation as to why I'm getting this compiler error, perhaps someone could point out why my approach here is "bad design" and what I should do to correct it. Thanks in advance!
Imagine if this compiled:
List<SedementaryMineral> list = new ArrayList<>();
list.put(new SedimentaryMineral());
List<IMineral> mineralList = list;
mineralList.add(new NonSedimentaryMineral());
for(SedementaryMineral m : list) {
System.out.println(m); // what happens when it gets to the NonSedimentaryMineral?
}
You have a serious issue there.
What you can do is this: List<? extends IMineral> mienralList = list
The problem is that Java generics are not covariant; List<SedimentaryMineral> does not extend/implement List<IMineral>.
The solution depends on precisely what you wish to do here. One solution would involve wildcards, but they impose certain limitations.
Here is what will work for you:
interface IRock
{
public List<? extends IMineral> getMinerals();
}
interface IMineral { }
class SedimentaryMineral implements IMineral { }
class SedimentaryRock implements IRock
{
private List<SedimentaryMineral> minerals;
public List<? extends IMineral> getMinerals()
{
return minerals;
}
}
Here I am using wildcard to denote that I allow list of everything that extends the basic interface to be returned from getMinerals. Note that I also changed some of your classes to interfaces so that everything will compile (I also removed the accessors of the classes so that I can put them in a single file, but you can add them back).
First, your code would work if you done something like
...
public interface IRock
{
public List<? extends IMineral> getMinerals();
}
...
Second, you can't do this directly because you wouldn't be able to guarantee type safety from what you insert inside your list. So, if you want anything that could extend Mineral inside your rock, do what I showed above. If you want that only a specific type of be inserted inside a rock, do something like
public interface IRock<M extends IMineral> {
public List<M> getMinerals();
}
public class SedimentaryRock implements IRock<SedimentaryMineral> {
public List<SedimentaryMineral> getMinerals()
{
return minerals;
}
}
You need to understand why this cannot work in general, and why it is a good thing to have the compiler complain here.
Assuming we have a class ParkingLot implements Collection<Cars> and since Car extends Vehicle, this would automatically make the ParkingLot also implement Collection<Vehicle>. Then I could put my Submarine into the ParkingLot.
Less funny, but simpler speaking: a collection of apples is not a collection of fruit. A collection of fruit may contain bananas, while a collection of apples may not.
There is a way out of this: using wildcards. A collection of apples is a collection of "a particular subtype of fruit". By forgetting which kind of fruit it was, you get what you intended: you know it's some kind of fruit you get out. At the same time, you can't be sure that you are allowed to put in arbitrary fruit.
In java, this is
Collection<? extends Fruit> collectionOfFruit = bagOfApples;
// Valid, as the return is of type "? extends Fruit"
Fruit something = collectionOfFruit.iterator().next();
// Not valid, as it could be the wrong kind of fruit:
collectionOfFruit.put(new Banana());
// To properly insert, insert into the bag of apples,
// Or use a collection of *arbitrary* fruit
Let me emphasize the difference again:
Collection<Fruit> collection_of_arbitrary_fruit = ...;
collection_of_arbitrary_fruit.put(new Apple());
collection_of_arbitrary_fruit.put(new Banana());
Must be able to store any fruit, apples and bananas.
Collection<? extends Fruit> collection_of_one_unknown_kind_of_fruit = ...;
// NO SAFE WAY OF ADDING OBJECTS, as we don't know the type
// But if you want to just *get* Fruit, this is the way to go.
Could be a collection of apples, a collection of banananas, a collection of green apples only, or a collection of arbitary fruit. You don't know which type of fruit, could be a mix. But they're all Fruit.
In read-only situations, I clearly recommend using the second approach, as it allows both specialized ("bag of apples only") and broad collections ("bag of mixed fruit")
Key to understanding this is to read Collection<A> as Collection of different kind of A, while Collection<? extends A> is a Collection of some subtype of A (the exact type however may vary).

Categories

Resources