Enforcing relationships between types safely - java

I am building a system for writing code about people who take pictures of birds (the real system isn't actually about that, I use birds here to replace the business logic that I can't post). I'm having trouble keeping my code type safe while also enforcing all of the relationships I want and avoiding the code becoming super messy. Here's what I have.
There are three types of birds
public interface BirdType {}
public class BlueJay implements BirdType {}
public class Cardinal implements BirdType {}
public class Canary implements BirdType {}
For each type of bird, there's a type of camera specialized in taking pictures of that bird, and a special bird call which attracts birds of that type.
public interface BirdCamera<BT extends BirdType> {}
public interface BirdCall<BT extends BirdType> {}
public class BlueJayCamera implements BirdCamera<BlueJay> {}
public class CardinalCamera implements BirdCamera<Cardinal> {}
public class CanaryCamera implements BirdCamera<Canary> {}
public class BlueJayCall implements BirdCall<BlueJay> {}
public class CardinalCall implements BirdCall<Cardinal> {}
public class CanaryCall implements BirdCall<Canary> {}
These parts are combined in the Photographer interface, which enforces a relationship between the parts of a photographer for its implementers.
public interface Photographer
<BT extends BirdType,
CAM extends BirdCamera<BT>,
CAL extends BirdCall<BT>>
{
CAM getPhotographersCamera();
CAL getPhotographersBirdCall();
void examineCamera(CAM camera);
}
and it is implemented by three classes like this, one for each bird:
public class BlueJayPhotographer implements Photographer<BlueJay, BlueJayCamera, BlueJayCall> {
#Override
public BlueJayCamera getPhotographersCamera() {
//Do bluejay specific logic
return new BlueJayCamera();
}
#Override
public BlueJayCall getPhotographersBirdCall() {
//Do bluejay specific logic
return new BlueJayCall();
}
#Override
public void examineCamera(BlueJayCamera camera) {
//Do bluejay specific logic
}
}
One desirable thing about this is that it prevents future mistakes by other developers. Someone else can't later create
public class ConfusedPhotographer implements Photographer<BlueJay, CardinalCamera, CanaryCall>
because the constraints in Photographer prevent it.
When a class wants to use a photographer, they invoke the PhotographerProvider
public class PhotographerProvider {
public Photographer get(int birdCode) {
if (birdCode == 0) {
return new BlueJayPhotographer();
}
else if (birdCode == 1) {
return new CardinalPhotographer();
}
else if (birdCode == 2) {
return new CanaryPhotographer();
}
else {
throw new IllegalArgumentException("Unsupported bird code: " + birdCode);
}
}
}
One such class that needs to use photographers in many of its methods is the NationalPark, one of the core classes of my application. Here's an example NationalPark method:
public void importantMethod(PhotographerProvider photographerProvider) {
// blah blah logic goes here
int mostCommonBirdType = 1;
Photographer typicalTourist = photographerProvider.get(mostCommonBirdType);
BirdCamera cam = typicalTourist.getPhotographersCamera();
typicalTourist.examineCamera(cam);
// blah blah more logic here
}
The compiler doesn't like the last line of that method, and produces a warning:
Unchecked call to 'examineCamera(CAM)' as a member of raw type 'birdexample.Photographer'
I don't want this warning, but I can't find a way to fix it that doesn't cause an error someplace else... I'd also like to avoid making a gigantic mess where every class in my entire program has to be paramaterized with a bird type, camera, and call. How can I do this (short of just suppressing/ignoring the warning)?

Your specific example can be done without warnings as follows:
Photographer<?, ?, ?> typicalTourist = photographerProvider.get(mostCommonBirdType);
foo(typicalTourist);
<BT extends BirdType,
CAM extends BirdCamera<BT>> void foo(Photographer<BT, CAM, ?> typicalTourist) {
CAM cam = typicalTourist.getPhotographersCamera();
typicalTourist.examineCamera(cam);
}
But I would caution about your design. Honestly, anything more than 1 type variable is pretty unwieldy. 2 is tolerable for cases like maps, but 3 is just getting a bit crazy - you end up filling half your line with the type declaration. 6 (the number you say you have in the real code) is thoroughly into the territory of hating yourself, hating your colleagues, or both.
Think carefully if you really need these generics - you might save a lot of eye strain if you can get rid of them.

In addition to Andy's answer, your PhotographerProvider#get method issues a compiler warning due to its raw return type Photographer. To fix that, you can use the following:
public class PhotographerProvider {
public Photographer<?, ?, ?> get(int birdCode) {
if (birdCode == 0) {
return new BlueJayPhotographer();
} else if (birdCode == 1) {
return new CardinalPhotographer();
} else if (birdCode == 2) {
return new CanaryPhotographer();
} else {
throw new IllegalArgumentException("Unsupported bird code: " + birdCode);
}
}
}

Your method PhotographerProvider.get return a raw Photographer of unknow type of camera and birds:
public Photographer get(int birdCode) {
[...]
}
you could instead write something like
public <BT extends BirdType, CAM extends BirdCamera<BT>, CAL extends BirdCall<BT>> Photographer<BT, CAM, CALL> get(int birdCode) {
[...]
}
So you get type safety. The problem is that at least with recent version of java you could choose any BT type when calling the get method and it would be same for the compiler.
In fact all this system doesn't bring you much type safety. For example you instanciate the right type actually with an integer. And when one use a photographer he has no idea of the bird type or camera so he doen't really benefit of all of this.
The only place where it provide an added value is that a photographer of given BT type has to use a Camera and all of a given type... but you could as well do it with only BT as parametrzed type:
public interface Photographer<BT extends BirdType>
{
BirdCamera<BT> getPhotographersCamera();
BirdCall<BT> getPhotographersBirdCall();
void examineCamera(BirdCamera<BT> camera);
}
And you get method would directly get a BirdType:
public <BT extends BirdType> Photographer<BT> get(BT birdType) {
[...]
}

Related

How to "map" two different object inheritance trees without instanceof

I have a design question I can't get a good solution for. This is my problem:
There are two different object "trees" which need to processed together. Object tree one:
AbstractObjectTreeOne with Sub1ObjectTreeOne and Sub2ObjectTreeOne
AbstractObjectTreeTwo with Sub1ObjectTreeTwo and Sub2ObjectTreeTwo
I now have a method where I get a list of AbstractObjectTreeOne and a list of AbstractObjectTreeTwo. They are exact the same size and "match" to each other by name. So I can loop through the objects in the list of AbstractObjectTreeOne and get the according AbstractObjectTreeTwo by name.
Now it should be validated if the "matching" objects (by name) really match to each other, so the current code contains a lot of instanceof stuff. Example:
if (!(objectOfAbstractObjectTreeOne instanceof Sub1ObjectTreeOne)) {
throw exception;
}
and then also in the same method
if (!(objectOfAbstractObjectTreeTwo instanceof Sub1ObjectTreeTwo)) {
throw exception;
}
After that, both parameters are cast to their "real" subtype to be further processed. This also does not feel very good.
This all feels not very object-oriented, but I currently do not have a good idea how to solve this. I tried the visitor pattern, but it only solves the instanceof issue in either AbstractObjectTreeOne or AbstractObjectTreeTwo and still contains a lot of instanceof.
Maybe some of you have a good idea about this kind of problem. Maybe it's easy to solve, but I do not have the right idea yet.
This is called OOO Principle Polymorfism.
No need to use instanceof. You have to create an interface and use it in the declaration of the tree. All subtypes should implement this interface, and you can call the required methods without typecasting.
This is an example.
public interface ObjectTreeOne { void payloadOne() {} }
public class Sub1ObjectTreeOne implements ObjectTreeOne { void payloadOne() {} }
public class Sub2ObjectTreeOne implements ObjectTreeOne { void payloadOne() {} }
List<ObjectTreeOne> objectTreeOne = new ArrayList<>();
objectTreeOne.add(new Sub1ObjectTreeOne());
objectTreeOne.add(new Sub2ObjectTreeOne());
public interface ObjectTreeTwo { void payloadTwo() {} }
public class Sub1ObjectTreeTwo implements ObjectTreeTwo { void payloadTwo() {} }
public class Sub2ObjectTreeTwo implements ObjectTreeTwo { void payloadTwo() {} }
List<ObjectTreeTwo> objectTreeTwo = new ArrayList<>();
objectTreeTwo.add(new Sub1ObjectTreeTwo());
objectTreeTwo.add(new Sub2ObjectTreeTwo());
for(int i = 0; i < 2; i++) {
ObjectTreeOne objectTreeOne = objectTreeOne.get(i);
ObjectTreeTwo objectTreeTwo = objectTreeTwo.get(i);
objectTreeOne.payloadOne();
objectTreeTwo.payloadTwo();
}

Java generics in List return type on a method inherited from multiple interfaces

I'm currently working at a company that has a diverse set of modules. In that company if you want to provide module internals you provide it via a java interface, that hides the actual implementing type and gives an interface for the requesting module. Now I want to have one provider to be able to provide data for multiple modules that expose different fields or methods of the actual internal data.
Therefore I have an internal Object, which has some data and I have an interface for each module that needs access to some but not strictly all fields. Finally I have an external object that implements all those interfaces and holds an instance of the internal object to delegate the method calls:
public class InternalObject {
public int getA() { return 0; }
public int getB() { return 0; }
}
public interface ModuleXObject {
int getA();
}
public interface ModuleYObject {
int getA();
int getB();
}
public class ExternalObject implements ModuleXObject, ModuleYObject {
private InternalObject _internal;
public int getA() { return _internal.getA(); }
public int getB() { return _internal.getB(); }
}
Now that is all fine and dandy, but if I want to provide - lets say - repository methods for finding a list of said objects typed for the correct module, I run into problems with how I can achieve that. I would wish for something like the following:
public interface ModuleXObjectRepository {
List<ModuleXObject> loadAllObjects();
}
public interface ModuleYObjectRepository {
List<ModuleYObject> loadAllObjects();
}
public class ExternalObjectRepository implements ModuleXObjectRepository, ModuleYObjectRepository {
public List<ExternalObject> loadAllObjects() {
// ...
}
}
This doesn't compile saying the return type is incompatible.
So my question is, if it is possible to achieve something like that and if, how?
I should note that I tried some different approaches which I want to include for completeness and to portray their downsides (in my eyes).
Approach 1:
public interface ModuleXObjectRepository {
List<? extends ModuleXObject> loadAllObjects();
}
public interface ModuleYObjectRepository {
List<? extends ModuleYObject> loadAllObjects();
}
public class ExternalObjectRepository implements ModuleXObjectRepository, ModuleYObjectRepository {
public List<ExternalObject> loadAllObjects() {
// ...
}
}
This approach is quite close to the solution I would prefer, but results in code like this:
List<? extends ModuleXObject> objects = repository.loadAllObjects();
Therefore requiring the user to include the "? extends" into each List-Declaration regarding to an invocation of loadAllObjects().
Approach 2:
public interface ModuleXObjectRepository {
List<ModuleXObject> loadAllObjects();
}
public interface ModuleYObjectRepository {
List<ModuleYObject> loadAllObjects();
}
public class ExternalObjectRepository implements ModuleXObjectRepository, ModuleYObjectRepository {
public List loadAllObjects() {
// ...
}
}
This approach just omits the generic in the ExternalObjectRepository and therefore reduces the type safety too much in my opinion. Also I haven't tested if this actually works.
Just to reharse, is there any possible way to define the loadAllObjects-method in a way that enables users to get lists that are typed with the objects for their respective module without
requiring "? extends" in the users code
degrading type safety in the repository implementation
using class/interface level generics
The challenge with allowing it to be typed as List<ModuleXObject> is that other code may hold is as a List<ExternalObject>.
All ExternalObject instances are ModuleXObject instances but the inverse is not true.
Consider the following additional class:
public class MonkeyWrench implements ModuleXObject{
//STUFF
}
MonkeyWrench instances are NOT ExternalObject instances but if one could cast a List<ExternalObject> to a List<ModuleXObject> one could add MonkeyWrench instances to this collection, and this causes a risk of run time class cast exceptions and ruins type safety.
Other code could very easily have:
for(ExternalObject externalObject:externalObjectRepository.loadAllObjects())
If one of those instances is a MonkeyWrench instance, run time class cast, which is what generics are meant to avoid.
The implication of ? extends ModuleXObject is that you can read any object from the collection as a ModuleXObject but you can't add anything to the collection as other code may have additional constraints on the collection that are not obvious/available at compile time.
I'd suggest in your case to use ? extends ModuleXObject as its semantics seem to align with what you want, namely pulling out ModuleXObject instances, e.g.
ModuleXObjectRepository repo = //get repo however
for(ModuleXObject obj : repo.loadAllObjects()){
//do stuff with obj
}

Java Factory using Generics, .class vs. .getClass()

I searched and tried now for more than a day and could not find a solution for a common problem I have in Java. The reason is obvious - Type erasure. But the question I have is: Is there really no good solution for this problem in Java? I am willing to investigate even more time since this kind of problem pops up every once in a time.
The error I get is:
The method doStrategy(capture#2-of ? extends I) in the type IStrategy<capture#2-of ? extends I> is not applicable for the arguments (I)
So I simplified the problem to the following example.
Imagine the model:
package model;
public interface I {
//there are actual 30 classes implementing I...
}
public class A implements I {
public void someSpecificMagicForA(){
System.out.println("A");
}
}
public class B implements I {
public void someSpecificMagicForB() {
System.out.println("B");
}
}
and the selection logic
package strategy;
import model.A;
public interface IStrategy<T> {
public void doStrategy(T t);
}
public class AStrategy implements IStrategy<A> {
#Override
public void doStrategy(A a) {
a.someSpecificMagicForA();
}
}
public class BStrategy implements IStrategy<B> {
#Override
public void doStrategy(B b) {
b.someSpecificMagicForB();
}
}
and a generic strategy factory
package strategy;
import java.util.HashMap;
import java.util.Map;
import model.A;
import model.B;
public class StrategyFactory {
static {
strategies.put(A.class, AStrategy.class);
strategies.put(B.class, BStrategy.class);
}
private static final Map<Class<?>, Class<? extends IStrategy<?>>> strategies = new HashMap<>();
#SuppressWarnings("unchecked") // I am fine with that suppress warning
public <T> IStrategy<T> createStategy(Class<T> clazz){
Class<? extends IStrategy<?>> strategyClass = strategies.get(clazz);
assert(strategyClass != null);
try {
return (IStrategy<T>) strategyClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}
And here is the test
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import model.A;
import model.B;
import model.I;
import strategy.IStrategy;
import strategy.StrategyFactory;
public class TestCases extends TestCase {
public void testWithConcreteType(){
B b = new B();
StrategyFactory factory = new StrategyFactory();
IStrategy<B> createStategy = factory.createStategy(B.class);
createStategy.doStrategy(b); //awesome
}
public void testWithGenericType(){
List<I> instances = createTestData(); // image this is the business data
StrategyFactory factory = new StrategyFactory();
for (I current : instances){
IStrategy<? extends I> createStategy = factory.createStategy(current.getClass());
createStategy.doStrategy(current); //meh
//The method doStrategy(capture#2-of ? extends I) in the type IStrategy<capture#2-of ? extends I>
//is not applicable for the arguments (I)
}
}
private List<I> createTestData(){
A a = new A();
B b = new B();
List<I> instances = new ArrayList<>();
instances.add(a);
instances.add(b);
return instances;
}
}
I have tried another approach using guava TypeTokens (https://github.com/google/guava/wiki/ReflectionExplained). But I did not manage to get this working since I really have no <T> since all I get is that Collection of instances implementing that interface.
I have a working and not so bad solution though using the Visitor-pattern. Since I there have the real classes
...visitor class
public void visit(A a){
doVisit(A.class, a); //private generic method now works of course
}
everything is fine again at compile time. But in this particular case it took me quite some time to implement that visitor for more than 30 sub-classes of I. So I would really like to have a better solution for the future.
Any comments are much appreciated.
The problem has nothing to do with erasure. It is that Java's type system is not strong enough to reason statically about the relationship between current and current.getClass() without some help.
In your code:
for (I current : instances){
IStrategy<? extends I> createStategy = factory.createStategy(current.getClass());
createStategy.doStrategy(current);
}
the result of current.getClass() is an object of type Class<? extends I>; that is, some subtype of I that we don't know statically. We the programmers know that whatever type it is, that's also the concrete type of current because we've read the documentation for getClass, but the type system doesn't know that. So when we get an IStrategy<? extends I>, all we know is that it's a strategy that works on some subtype of I, not necessarily I itself. Add to that the fact that wildcard types (the types with ?) are designed to lose even more information, and so the type system doesn't even know that our strategy accepts the same type as the result of getClass().
So to make the program typecheck, we need to (a) give the type system some non-wildcard name for the specific subtype of I that current is, and (b) convince it that the value of current actually has that type. The good news is, we can do both.
To give a name to a wildcard type, we can use a technique called "wildcard capture", where we create a private helper function whose only job is to give a specific type variable name to a type that would otherwise be a wildcard. We'll pull out the body of the test loop into its own function that critically takes a type parameter:
private <T> void genericTestHelperDraft1(Class<T> currentClass, T current) {
StrategyFactory factory = new StrategyFactory();
IStrategy<T> createStrategy = factory.createStategy(t);
createStrategy.doStrategy(current); // works
}
The job of this function is effectively to introduce the type parameter T, which lets Java know that we intend to refer to the same unknown type T everywhere we use it. With that information it can understand that the strategy we get from our factory works on the same type that our input class has, which is the same type current current has in the type signature.
Unfortunately, when we go to call this method we'll still get a compile error:
for (I current : instances){
genericTestHelperDraft1(current.getClass(), current);
// Type error because current is not of type "capture of ? extends I"
}
The problem here is that the type system doesn't know that current has its own type! Java's type system doesn't understand the relationship between current and current.getClass(), so it doesn't know that whatever type current.getClass() returns, we can treat current as a value of that type. Fortunately we can fix that with a simple downcast, since we (the programmers) do know that current has its own type. We have to do this within our helper, since outside the helper we don't have any name for the subtype we want to assert that current has. We can change the code like so:
private <T> void genericTestHelperDraft2(Class<T> t, Object current) {
T currentDowncast = t.cast(current);
StrategyFactory factory = new StrategyFactory();
IStrategy<T> createStrategy = factory.createStategy(t);
createStrategy.doStrategy(currentDowncast);
}
Now we can change the loop within the test to:
for (I current : instances){
genericTestHelperDraft2(current.getClass(), current);
}
and everything works.
Generic is a compile time feature and you can only work with what the compiler can determine as safe.
Note the type is not erased in all cases. You can get the type of AStrategy and BStrategy for example as these are concrete types which are not dynamic.
AStrategy as = new AStrategy();
for(AnnotatedType asc : as.getClass().getAnnotatedInterfaces()) {
Type type = asc.getType();
System.out.println(type);
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
for (Type t : pt.getActualTypeArguments()){
System.out.println(t); // class A
}
}
}
prints
IStrategy<A>
class A
whether all this code makes your solution simpler, I don't know but you can get the type of IStrategy which is being implemented provided it is known when the class was compiled.
After Peter´s answer and some more research I am pretty sure this is not possible in Java without enhancing the model in one way or another. I decided to stay with the Visitor-pattern since the compile time checks are worth the extra code in my opinion.
So here is what I implemented in the end (also I´m sure you all know the Visitor-pattern - just to be complete).
public interface I {
public void acceptVisitor(IVisitor visitor);
//there are actual 30 classes implementing I...
}
public interface IVisitor {
public void visit(A a);
public void visit(B b);
}
public void testWithGenericType(){
List<I> instances = createTestData(); // image this is the business data
StrategyFactory factory = new StrategyFactory();
Visitor visitor = new Visitor(factory);
for (I current : instances){
current.acceptVisitor(visitor);
}
}
class Visitor implements IVisitor {
private final StrategyFactory factory;
public Visitor(StrategyFactory factory) {
this.factory = factory;
}
private <T> void doVisit(Class<T> clazz, T t){
IStrategy<T> createStategy = factory.createStategy(clazz);
createStategy.doStrategy(t);
}
#Override
public void visit(A a) {
doVisit(A.class, a);
}
#Override
public void visit(B b) {
doVisit(B.class, b);
}
}
Hope this maybe helps someone else.
Regards,
Rainer

Why can't I extend an interface "generic method" and narrow its type to my inherited interface "class generic"?

I show an example of what I mean which, is easier.
Imagine the generic type C means Color type: So for
visual simplification assume C is C extends Color
interface Screen {
<C> Background<C> render(Plane<C> plane);
}
interface MonochromeScreen<C> extends Screen{
#Override
Background<C> render(Plane<C> plane);
}
This would throw a name clash compilation error explaining that both have the same type erasure but are not overridable.
But I cannot understand why we could not simply allow overriding the signature as long as it is more restrictive. I mean, after all, the only difference is the scope of the generic type, in Screen is method-wide and in MonochromeScreen is class-wide.
It would not make sense to allow a child method to override as a method-scoped generic when its parent enforces coherence at class level, but I think it does otherwise: My parent interface could have 20 methods with unrelated generic types, but my child class would force them all to be the same as a non-incompatible extra specification/contract (which is what any extended interface does), After all, a monochrome sccreen is still an screen, as it can be painted with any color, I am just enforcing that color, whichever it is, to be it consistent accross the other functions of the child, Just narrowing the possibilities at class level, not method level.
Is there any fundamentally wrong assumption for considering the feature?
EDIT: I accepted Sotirios Delimanolis answer for him spotted the right trouble very cleverly and I was not asking for a solution, but for those who want to know how to overcome the situation there is a trick explained in my own answered answer
Here's where this breaks:
MonochromeScreen<Red> redScreen = ...;
Screen redScreenJustAScreen = redScreen;
Plane<Blue> bluePlane = null;
redScreenJustAScreen.<Blue>render(bluePlane);
If what you suggested worked at compile time, the snippet above would presumably have to fail at runtime with a ClassCastException because the object referenced by redScreenJustAScreen expects a Plane<Red> but received a Plane<Blue>.
Generics, used appropriately, are supposed to prevent the above from happening. If such overriding rules were allowed, generics would fail.
I don't know enough about your use case, but it doesn't seem like generics are really needed.
The reason this is not allowed is that it violates the Liskov substitution principle.
interface Screen {
<C> Background<C> render(Plane<C> plane);
}
What this means is that you can call render() at any time with an arbitrary type as C.
You can do this for example:
Screen s = ...;
Background<Red> b1 = s.render(new Plane<Red>());
Background<Blue> b2 = s.render(new Plane<Blue>());
Now if we look at MonochromeScreen:
interface MonochromeScreen<C> extends Screen{
Background<C> render(Plane<C> plane);
}
What this declaration says is: you must choose exactly one type as C when you create an instance of this object and you can only use that for the whole life of that object.
MonochromeScreen<Red> s = ...;
Background<Red> b1 = s.render(new Plane<Red>());
Background<Blue> b2 = s.render(new Plane<Blue>()); // this won't compile because you declared that s only works with Red type.
Therefore it follows that Screen s = new MonochromeScreen<Red>(); is not a valid cast, MonochromeScreen cannot be a subclass of Screen.
Okay, let's turn this around a bit. Let's assume that all colors are instances of a single Color class and not separate classes. What would our code look like then?
interface Plane {
Color getColor();
}
interface Background {
Color getColor();
}
interface Screen {
Background render(Plane plane);
}
So far, so good. Now we define a monochrome screen:
class MonochromeScreen implements Screen {
private final Color color; // this is the only colour we have
public Background render(Plane plane) {
if (!plane.getColor().equals(color))
throw new IllegalArgumentException( "I can't render this colour.");
return new Background() {...};
}
}
This would compile fine and would have more or less the same semantics.
The question is: would this be good code? After all, you can still do this:
public void renderPrimaryPlanes(Screen s) { //this looks like a good method
s.render(new Plane(Color.RED));
s.render(new Plane(Color.GREEN));
s.render(new Plane(Color.BLUE));
}
...
Screen s = new MonochromeScreen(Color.RED);
renderPrimaryPlanes(s); //this would throw an IAE
Well, no. That's definitely not what you'd expect from an innocent renderPrimaryPlanes() method. Things would break in unexpected ways. Why is that?
It's because despite it being formally valid and compileable, this code too breaks the LSP in exactly the same way the original did. The problem is not with the language but with the model: the entity you called Screen can do more things than the one called MonochromeScreen, therefore it can't be a superclass of it.
FYI: That is the only way I found to solve the case and pass the overriding of the method (If the design pattern has a name, I'd appreciate to know ir! It's in the end the way to extend an interface with generic methods to make it class-generic. And still you can type it with the parent type (aka Color) to use it as the raw general type old interface.):
Screen.java
public interface Screen {
public interface Color {}
public class Red implements Color {}
public class Blue implements Color {}
static Screen getScreen(){
return new Screen(){};
}
default <C extends Color> Background<C> render(Plane<C> plane){
return new Background<C>(plane.getColor());
}
}
MonochromeScreen.java
interface MonochromeScreen<C extends Color> extends Screen{
static <C extends Color> MonochromeScreen<C> getScreen(final Class<C> colorClass){
return new MonochromeScreen<C>(){
#Override public Class<C> getColor() { return colorClass; };
};
}
public Class<C> getColor();
#Override
#SuppressWarnings("unchecked")
default Background<C> render(#SuppressWarnings("rawtypes") Plane plane){
try{
C planeColor = (C) this.getColor().cast(plane.getColor());
return new Background<C>(planeColor);
} catch (ClassCastException e){
throw new UnsupportedOperationException("Current screen implementation is based in mono color '"
+ this.getColor().getSimpleName() + "' but was asked to render a '"
+ plane.getColor().getClass().getSimpleName() + "' colored plane" );
}
}
}
Plane.java
public class Plane<C extends Color> {
private final C color;
public Plane(C color) {this.color = color;}
public C getColor() {return this.color;}
}
Background.java
public class Background<C extends Color> {
private final C color;
public Background(C color) {this.color = color;}
public C getColor() {return this.color;}
}
MainTest.java
public class MainTest<C> {
public static void main(String[] args) {
Plane<Red> redPlane = new Plane<>(new Red());
Plane<Blue> bluePlane = new Plane<>(new Blue());
Screen coloredScreen = Screen.getScreen();
MonochromeScreen<Red> redMonoScreen = MonochromeScreen.getScreen(Red.class);
MonochromeScreen<Color> xMonoScreen = MonochromeScreen.getScreen(Color.class);
Screen redScreenAsScreen = (Screen) redMonoScreen;
coloredScreen.render(redPlane);
coloredScreen.render(bluePlane);
redMonoScreen.render(redPlane);
//redMonoScreen.render(bluePlane); --> This throws UnsupportedOperationException*
redScreenAsScreen.render(redPlane);
//redScreenAsScreen.render(bluePlane); --> This throws UnsupportedOperationException*
xMonoScreen.render(new Plane<>(new Color(){})); //--> And still I can define a Monochrome screen as of type Color so
System.out.println("Test Finished!"); //still would have a wildcard to make it work as a raw screen (not useful
//in my physical model but it is in other abstract models where this problem arises
}
}
Exception thrown when adding blue plane in redScreen:
java.lang.UnsupportedOperationException: Current screen implementation
is based in mono color 'Red' but was asked to render a 'Blue' colored
plane
I do not think your code is what you want, which is why you are getting errors.
I think this is what you want
public interface Screen<C> {
Background<C> render(Plane<C> plane);
}
and
public interface MonochromeScreen<C> extends Screen<C> {
Background<C> render(Plane<C> plane);
}
What you may be mistaken in thinking is that because <C> is both interfaces it is the same thing. It is not.
this
public interface MonochromeScreen<HI> extends Screen<HI> {
Background<HI> render(Plane<HI> plane);
}
is exactly the same as the code above. C and HI are just names for the generic placeholders. by extending Screen<C> with extends Screen<HI> we tell java that C is the same placeHolder as HI so it will do it's magic.
In your code
<C> Background<C> render(Plane<C> plane);
we have declared a brand new place holder that only has context in that method.
so we could write this code
MonochromeScreen<String> ms;
ms.render(new Plane<Banana>());
<C> Background<C> render(Plane<C> plane);
is redefined as
Background<Banana> render(Plane<Banana> plane);
but
Background<C> render(Plane<C> plane);
is redefined as
Background<String> render(Plane<String> plane);
which conflict and so java gives you an error.

Implementing multiple instances of the same generic Java interface with different generic types?

I am designing an event-driven system and am running into some basic API problems regarding generics.
I woud like all events to extend BaseEvent:
// Groovy pseudo-code
abstract BaseEvent {
Date occurredOn
BaseEvent() {
super()
this.occurredOn = new Date() // Now
}
}
And I would like all event listeners to implement some basal interface:
interface EventListener<EVENT extends BaseEvent> {
void onEvent(EVENT event)
}
So this works great for simple listeners that only handle a single type of event:
class FizzEvent extends BaseEvent { ... }
class FizzEventListener implements EventListener<FizzEvent> {
#Override
void onEvent(FizzEvent fizzEvent) {
...
}
}
But I will have some listeners that need to handle multiple types of events:
class BuzzEvent extends BaseEvent { ... }
// So then, ideally:
class ComplexListener implements EventListener<FizzEvent>,
EventListener<BuzzEvent> {
#Override
void onEvent(FizzEvent fizzEvent) {
...
}
#Override
void onEvent(BuzzEvent buzzEvent) {
...
}
}
But this produces compiler errors:
Name clash: The method onEvent(EVENT) of type EventListener has the same erasure as onEvent(EVENT) of type EventListener but does not override it
Any ideas what the solution is for handling multiple events?
The problem you're running into is called Type Erasure, which is how Java implements generics. This means that, for Java, the following lines of code:
#Override
void onEvent(FizzEvent fizzEvent) {
...
}
#Override
void onEvent(BuzzEvent buzzEvent) {
...
}
really look like this:
#Override
void onEvent(BaseEvent fizzEvent) {
...
}
#Override
void onEvent(BaseEvent buzzEvent) {
...
}
Notice that the type information has been 'erased' and only the super type BaseEvent remains as the type parameter for both methods, which causes ambiguity and won't work.
If the extends keyword had not been used, it would only see Object instead, but would still run into the same problem.
This is in contrast to C#, which uses Type Reification to implement generics and can know the difference of types at runtime.
In other words, if you ask Java whether a List<Dog> is the same kind of list as a List<Car>, Java would say "yes" because it doesn't know any better at runtime, while C# would say "no" because it retains type information.
Any ideas what the solution is for handling multiple events?
You will need to use different method names or signatures if you want to use the same listener interface (e.g. onDogBarkEvent(Dog d), onCatMeowEvent(Cat c) or perhaps create separate listener interfaces for different kinds of events (e.g. DogBarkListener, CatMeowListener).
This should point you in the right direction with a few Java options.
That aside, if you really feel strongly about your choice and are also free to choose your programming language, then you could consider taking C# for a spin and see if it works better for you.
a possible solution would be to skip generics and have an explicit "supports" method:
public FooListener implements Listener {
public <T extends BaseEvent> boolean supports(Class<T> clazz) {
//decide
}
public void handle(BaseEvent baseEvent) {
//handle
}
}
this, in combination with some abstract classes with generics for the "simple" cases, should do the trick:
private Class<S> clazz;
public Class<S> getClazz() {
if(clazz==null) {
ParameterizedType superclass =
(ParameterizedType)getClass().getGenericSuperclass();
clazz = (Class<S>) superclass.getActualTypeArguments()[0];
}
return clazz;
}
public boolean supports(Class clazz) {
return clazz!=null && clazz == getClazz();
In java 8
public class ComplexListener
{
public final EventListener<FizzEvent> fizzListener = fizzEvent ->
{
...
}
...
use complexListener.fizzListener whenever an EventListener<FizzEvent> is needed.
(Without java8, you can use anonymous class for the same effect, just more verbose.)
Another way in java8 is through method reference
public class ComplexListener
{
public void handleFizzEvent(FizzEvent fizzListener)
{
...
}
use complexListener::handleFizzEvent whenever an EventListener<FizzEvent> is needed.
In java generics, it is explicitly forbidden that an object can be both Foo<A> and Foo<B> (A!=B); i.e. Foo<A> and Foo<B> are mutually exclusive. Many reasons can be raised, but the most important one I think is because of capture conversion -- given a Foo<?> object, the compiler assumes it is a Foo<X> of a unique X. Therefore no object can be Foo<A> & Foo<B> (irrespective of reification).

Categories