Need derived class for java generics declaration - java

I've run into a sticky problem that I can't seem to solve with java generics. This is a bit complicated, but I couldn't think of a simpler scenario to illustrate the problem... Here goes:
I have a Processor class that requires a Context. There are different types of Context; most processors just need any abstract Context, but others require a specific subclass. Like this:
abstract class AbstractProcessor<C extends Context> {
public abstract void process(C context);
}
class BasicProcessor extends AbstractProcessor<Context> {
#Override
public void process(Context context) {
// ... //
}
}
class SpecificProcessor extends AbstractProcessor<SpecificContext> {
#Override
public void process(SpecificContext context) {
// ... //
}
}
Ok, cool: Processors can declare the type of Context they need, and they can assume the right type will be passed into process() without casting.
Now, I have a Dispatcher class that owns a mapping of Strings to Processors:
class Dispatcher<C extends Context> {
Map<String, AbstractProcessor<? super C>> processorMap = new HashMap<String, AbstractProcessor<? super C>>();
public void registerProcessor(String name, AbstractProcessor<? super C> processor) {
processorMap.put(name, processor);
}
public void dispatch(String name, C context) {
processorMap.get(name).process(context);
}
}
Ok, so far so good! I can create a Dispatcher for a specific type of Context, then register a batch of processors that may expect any abstraction of that Context type.
Now, here's the problem: I want the abstract Context type to own the Dispatcher, and derived Context types should be able to register additional Processors. Here's the closest I could find to a working solution, but it doesn't fully work:
class Context<C extends Context> {
private final Dispatcher<C> dispatcher = new Dispatcher<C>();
public Context() {
// every context supports the BasicProcessor
registerProcessor("basic", new BasicProcessor());
}
protected void registerProcessor(String name, AbstractProcessor<? super C> processor) {
dispatcher.registerProcessor(name, processor);
}
public void runProcessor(String name) {
dispatcher.dispatch(name, this); // ERROR: can't cast Context<C> to C
}
}
// this is totally weird, but it was the only way I could find to provide the
// SpecificContext type to the base class for use in the generic type
class SpecificContext extends Context<SpecificContext> {
public SpecificContext() {
// the SpecificContext supports the SpecificProcessor
registerProcessor("specific", new SpecificProcessor());
}
}
The problem is that I need to declare a generic Dispatcher in the base Context class, but I want the type-variable to refer to the specific derived type for each Context sub-type. I can't see a way to do this without duplicating some code in each Context subclass (specifically, the construction of the Dispatcher and the registerProcessor method). Here's what I think I really want:
Dispatcher<MyRealClass> dispatcher = new Dispatcher<MyRealClass>();
Is there a way to declare the generic type of an object with the type of the SUBCLASS of the declaring class?
Yes, I can address this problem with a little bit of low-risk casting, so this is mostly an academic question... But I'd love to find a solution that just works top-to-bottom! Can you help? How would you approach this architecture?
UPDATE:
Here's the full source, updated to incorporate Andrzej Doyle's suggestion to use <C extends Context<C>>; it still doesn't work, because Context<C> != C:
class Context<C extends Context<C>> {
private final Dispatcher<C> dispatcher = new Dispatcher<C>();
public Context() {
// every context supports the BasicProcessor
registerProcessor("basic", new BasicProcessor());
}
protected void registerProcessor(String name, AbstractProcessor<? super C> processor) {
dispatcher.registerProcessor(name, processor);
}
public void runProcessor(String name) {
dispatcher.dispatch(name, this); // ERROR: can't cast Context<C> to C
}
}
// this is totally weird, but it was the only way I could find to provide the
// SpecificContext type to the base class for use in the generic type
class SpecificContext extends Context<SpecificContext> {
public SpecificContext() {
// the SpecificContext supports the SpecificProcessor
registerProcessor("specific", new SpecificProcessor());
}
}
abstract class AbstractProcessor<C extends Context<C>> {
public abstract void process(C context);
}
class BasicProcessor extends AbstractProcessor {
#Override
public void process(Context context) {
// ... //
}
}
class SpecificProcessor extends AbstractProcessor<SpecificContext> {
#Override
public void process(SpecificContext context) {
// ... //
}
}
class Dispatcher<C extends Context<C>> {
Map<String, AbstractProcessor<? super C>> processorMap = new HashMap<String, AbstractProcessor<? super C>>();
public void registerProcessor(String name, AbstractProcessor<? super C> processor) {
processorMap.put(name, processor);
}
public void dispatch(String name, C context) {
processorMap.get(name).process(context);
}
}

It sounds like your problem is that you need the generics to refer to the specific exact type of the subclass, rather than inheriting the generic definition from the parents. Try defining your Context class as
class Context<C extends Context<C>>
Note the recursive use of the generic parameter - this is a bit hard to wrap one's head around, but it forces the subclass to refer to exactly itself. (To be honest I don't quite fully get this, but so long as you remember that it works, it works. For reference, the Enum class is defined in exactly the same way.) There's also a section in Angelika Langer's Generics FAQ that covers this.
This way the compiler gets more information about exactly what types are permissable, and should allow your case to compile without the superfluous casting.
UPDATE: Having thought about this a bit more, my above comments were along the right track but were not entirely on the money. With self-recursive generic bounds, as above, you can never really use the actual class you define them on. I'd actually never fully noticed this before, as by luck or judgement I'd apparently always used this in the right point of the class hierarchy.
But I took the time to try and get your code to compile - and realised something. The class with these bounds can never be referred to as itself, it can only ever be referred to in the context of a specific subclass. Consider the definition of BasicProcessor for example - Context appears ungenerified in the generic bounds for AbstractProcessor. To prevent a raw type from appearing, it would be necessary to define the class as:
class BasicProcessor extends AbstractProcessor<Context<Context<Context<...
This is avoided with subclasses because they incorporate the recursiveness in their definition:
class SpecificContext extends Context<SpecificContext>
I think this is fundamentally the problem here - the compiler cannot guarantee that C and Context<C> are the same types because it doesn't have the required special-casing logic to work out that the two are actually an equivalent type (which can only actually be the case when the wilcard chaining is infinite, since in any non-infinite sense the latter is always one level deeper than the first when expanded).
So it's not a great conclusion, but I think in this case your cast is needed because the compiler is unable to derive the equivalence for itself otherwise. Alternatively, if you were using a concrete subclass of Context in a similar position the compiler is able to work it out and this would not be a problem.
If you do happen to find a way to get this working without casting or having to insert a dummy subclass then please report back - but I can't see a way to do that, that would work with the syntax and semantics available to Java's generics.

Related

Cannot override method that takes in parameter of inner class of generic

Ok, I'll try to explain this as cleanly as I can.
I've created a generic abstract controller class that has a method hasCreatePermissions that looks something like this:
public abstract class ApplicationController<
AppEntity extends ApplicationEntity,
AppService extends ApplicationService<AppEntity>,
DTOManager extends ApplicationDTOManager
> {
// Other methods, properties, etc...
public boolean hasCreatePermissions(DTOManager.CreationRequest requestBody, Optional<UUID> requestingUser) {
return false;
}
}
Essentially, I want any class that overrides this method to be able to use its own DTOManager class as the parameter when it overrides this method.
The generic ApplicationDTOManager class looks like
public abstract class ApplicationDTOManager {
public abstract class CreationRequest {}
public abstract class CreationResponse {}
}
and any class that inherits ApplicationDTOManager can add classes that extend CreationRequest and CreationResponse for their own implementation of respective DTOs.
However, lets say I try to extend it with a UserResource class (assume UserDTOManager exists with an implementation for CreationRequest):
#RestController
public class UserResource extends ApplicationController<
User,
UserService<User>,
UserDTOManager
> {
#Override
public boolean hasCreatePermissions(UserDTOManager.CreationRequest requestBody, Optional<UUID> requestingUser) {
// Stuff
}
}
I'm told that this does not override any super class methods. Why? Is there any way to achieve this as I did not want to pass too many generics to my ApplicationController class, but also cannot have a constructor.
class ApplicationController<
AppEntity extends ApplicationEntity,
No, stop right there. This is declaring a type variable with the bound rules: "Must be either ApplicationEntity or any subtype thereof" and you named it AppEntity. This is going to make your head go in circles when you read the code later, 'I keep confusing reified types with type variables' comprises 95% of all confusion about generics. I know it seems unreadable, but there really is just only one way to go about it, and that is to use single capital letters for all your type vars. So let's fix that right now:
public abstract class ApplicationController<
E extends ApplicationEntity,
S extends ApplicationService<E>,
M extends ApplicationDTOManager> {
Which then immediately lets us see a big problem in the next line:
public boolean hasCreatePermissions(M.CreationRequest requestBody) {}
Of course that can't work. M is a type variable, at compile time the compiler has no idea what type it is. It could be bound to some type that doesn't even exist yet right now. You can't ask for a reified inner type on a type variable. You can of course simply talk about ApplicationDTOManager.CreationRequest and that's presumably what you want.
Alternatively, you're thinking that subtypes of ApplicationDTOManager may also want to subclass ApplicationDTOManager.CreationRequest. This is possible, but then all ApplicationDTOManager types need to carry their associated CreationRequest type as a type variable. We thus fix a few things:
public class ApplicationDTOManager<R extends ApplicationDTOManager.CreationRequest> {
public static class CreationRequest {}
}
You may have a non-static inner class named CreationRequest. I'm going to stop you again on that - non-static inners have an invisible inner field of their outer's type, and combining that invisible voodoo magic with generics just doesn't work. Don't do it. You can explicitly make that field if you must have it, and make a constructor that takes it. This is what javac generates for you if you don't add static to your inner classes. But by making it explicit, you take control of the generics, which you have to here, and avoid confusion which given the nature of the question seems pertinent.
I'm told that this does not override any super class methods. Why?
Java's method names include all their erased types. The name of this method:
class List<T extends Number> {
int hello(String name, boolean[] hi, T arg) throws SQLException {}
}
is, as far as the JVM is concerned, hello(Ljava/lang/String;[ZLjava/lang/Number;)I.
Yeah, no, really. javap -c -v a class file and you'll see it. (I is integer, [ is array, Z is boolean, and Ltxt; encodes ref type names in JVM style, e.g. with slashes and dollars instead of dots). It's written name(params)ret.
If you then subtype something and introduce a method whose erased JVM name is identical, you're overriding. If you don't, it is not an override. Merely an overload. Overrides are dynamically dispatched. But overloads are not - The names are all linked up at compile time. However, for any given 'JVM method name', the lookup is done dynamically based on the receiver type. In other words:
class Fruit {
void hi(Fruit f) { System.out.println("Fruit Fruit"); }
void hi(Apple a) { System.out.println("Fruit Apple"); }
}
class Apple extends Fruit {
void hi(Fruit f) { System.out.println("Apple Fruit"); }
void hi(Apple a) { System.out.println("Apple Apple"); }
}
Fruit f = new Fruit();
Fruit a = new Apple();
a.hi(a);
Will print Apple Fruit. You'd think it should print Apple Apple perhaps - we are calling apple's hi passing an apple, no? But the invocation a.hi(a) is invoking the method named hi(Lfruit;)V (because the type of a is Fruit). The receiver variable (a) has compile time type Fruit, but its real type is Apple. So, which of the hi(Lfruit;)V methods is chosen is done with dynamic dispatch - you get apple's hi(Fruit). Deciding between going with hi(Fruit) and hi(Apple) is done by the compiler only. Given that the type of the expression a is Fruit, you get hi(Fruit). The fact that if you resolve this expression, you get an object whose .getClass() returns Apple.class, doesn't change this.
Hence, what you wrote, does not override. Different name, even if you erase.
Toss generics in the mix and it gets funky. But you can do this.
public abstract class ApplicationDTOManager<I extends CreationRequest, O extends CreationResponse> {
public abstract static class CreationRequest {}
public abstract static class CreationResponse {}
}
public abstract class ApplicationController<
E extends ApplicationEntity,
S extends ApplicationService<AppEntity>,
I extends CreationRequest,
O extends CreationResponse,
M extends ApplicationDTOManager<I, O>
>
// heck that's probably too many, at some point generics aren't worth it
{
public abstract boolean hasCreatePermissions(I requestBody);
}
#RestController
public class UserResource extends ApplicationController<
User,
// UserService<User>, // this seems wrong!
UserService, // UserService should extends ApplicationService<User>
UserDTOManager.CreationRequest,
UserDTOManager.CreationResponse,
UserDTOManager> {
#Override
public boolean hasCreatePermissions(UserDTOManager.CreationRequest requestBody, Optional<UUID> requestingUser) {
// Stuff
}
}
Not sure all this is worth the pain, but, if you insist on linking all this together with generics, the above is the only way. You cannot express the notion 'has an inner type that is a subtype of CreationRequest' is a generics bound.
If you override a method you cannot have a different signature, If the method you override requires a DTOManager.CreateRequest you cannot use a child class within the override method.
You have to "support" all types of input that the parent method could take.
I believe that you need this because the permission validation relies on methods or fields of the child class. If so you should implement it inside the child class.

Generically typed visitor getting called on wrong types

I am using a generic abstract class of a framework AbstractProcessor<T extends CtElement> which is basically a visitor that visits all elements T. There is a method
public void process(T element)
that acts upon all elements of the specified type and does something.
I then have concrete class implementations of this AbstractProcessor that I create using some sort of factory pattern, while holding a list of the common supertype AbstractProcessor to then call the process-method via polymorphism on them. One of these concrete classes might be smth like XYZProcessor<T extends CtElement> extends AbstractProcessor<T>.
I now create these concrete processors like new XYZProcessor<CtNamedElement>() where CtNamedElement is a subtype of CtElement, so the process-method of the XYZProcessor only gets called with CTNamedElements. But the process-method seems to get called for all visisted elements of type CtElement, NOT only for those of the type CtNamedElement as I want to.
Does anyone know what is happening here?
EDIT: relevant code:
creating processors like this
case CLASS:
//CtClass is subtype of CtNamedElement
this.setProcessor(new AnnotatedWithProcessor<CtClass>(targetName, attributeMappings, metapackage));
break;
Class definition:
public class AnnotatedWithProcessor<T extends CtNamedElement> extends AbstractProcessor<T> {
#Override
public void process(T element) {
//do stuff here with elements of correct type
}
And then calling the processors like this:
//this gets set with a concrete case like above
AbstractProcessor<?> processor;
...
//this astModel gets processed with the respective processor,
//where I expect the process method only getting called for the correct types (in this case only when coming over elements of type CtClass),
//but the method gets called for all types of CtNamedElement, not only for those of type CtClass
this.astModel.processWith(processor);
Seems like you're doing so illegal casting somewhere, but it's hard to tell without seeing more of your implementation. Who is calling XYZProcessor::process? Why do you expect defining a generic interface to perform any sort of filtering?
Going to take a shot in the dark here, but it seems like what you probably want is a lookup map for types versus processors. This kind of code can get messy, so it's a good idea to limit access to this mapping using well tested methods:
private final Map<Class, Object> processorMap = new HashMap<Class, Object>();
public <T extends CtElement> void putProcessor(
Class<T> elementClass,
AbstractProcessor<? extends T> processor
) {
processorMap.put(elementClass, processor);
}
#SuppressWarnings("unchecked")
public <T extends CtElement> AbstractProcessor<? extends T> getProcessor(
Class<T> elementClass,
) {
return (AbstractProcessor<? extends T>) processorMap.get(elementClass);
}
Now when you search for a processor, you can use this map to find the correct implementation to call:
public void <T extends CtElement> process(T element) {
getProcessor(element.getClass()).process(element);
}
You may want to extend this to allow multiple implementations for each class, or filter the processorMap for all valid processors for element.getClass() using Class::isInstance and then processing for each valid key.

Is there any way to define a generic method in a super class to reference the inheriting class's type?

When defining something like this method:
class State {
public void addOperator(Operator<? extends State> op) {
}
}
Is there any way to define it in this way:
class State {
public void addOperator(Operator<? extends this.getClass()> op) {
}
}
So that any class inherting from State forces all passed in value to conform to its class-type.
I know that the above wont work, but is there anyway to force a generic wild-card to at least match the current classes type?
You already accepted an answer that claims your desired constraint can't be expressed in Java. If I understand the requirements correctly, the following solution comes close.
// You didn't flesh out what an operator does or provides,
// so I'll just make something up.
interface Operator<T> {
void apply(T arg);
}
// Request that a derived type provide its own type as a type
// parameter, per the Curiously Recurring Template Pattern (CRTP).
abstract class State<T extends State> {
public void addOperator(Operator<? extends T> op) {
final PrintStream s = System.out;
s.print("Received an operator of concrete type ");
s.print(op.getClass().getName());
s.println('.');
}
}
final class DerivedState extends State<DerivedState> {
}
public class Driver {
public static void main(String[] args) {
DerivedState ds = new DerivedState();
ds.addOperator(new Operator<DerivedState>() {
// ...
});
// And the following will not compile:
ds.addOperator(new Operator<Integer>() { /* ... */ });
}
}
Note that DerivedType's apply() method will only accept an Operator argument whose type parameter is DerivedType—or some type derived from DerivedType, but since DerivedType is final, no other such types exist.
What we can't do—which may be what Chris was alluding to—is mandate that the type parameter provided to State is in fact the derived type itself. We can't prevent the following definition, where one class supplies another as the type parameter for State:
final class AnotherDerivedState extends State<DerivedState> {
}
Here, one could call AnotherDerivedState#addOperator() with an Operator<DerivedState>, which is obviously not of type Operator<AnotherDerivedState>.
You can't prevent someone from deriving from State "incorrectly", but if you assume that people will follow the intended derivation pattern, you can help them use the rest of your library safely.
A subclass can be used instead of the superclass at any time because of the inclusion polymorphism.
Try writing
State.class
to get the Class object of State.
class State {
public void addOperator(Operator<? extends State.class> op) {
}
}
Although I am not sure that Class object inherit among them, they use generics, Class<T>.
So let's say we have class Base, and class Derived extends Base.
Class<Derived> probably does extends Class<Base>.
Instead, this scenario looks more promising
class State {
public void addOperator(Operator<Class<? extends State>> op) {
}
}

Java name clash, have the same erasure, neither hides the other

I am getting this name clash error and i don't know how should i solve the problem.
I have two classes and i am using overloaded method "createSensors". To simplify here is the code that generates the problem:
public abstract class ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassA> list) {
List<Sensor> sensors = new ArrayList<Sensor>();
for (ClassA s : list) {
sensors.add(s.getSensor());
}
return sensors;
}
}
public abstract class ClassB extends ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassB> list) {
List<Sensor> sensors = new ArrayList<Sensor>();
for (ClassB s : list) {
sensors.add(s.getSensor());
}
return sensors;
}
}
General answer :
Apart from the problem of the same implementation here, the core of the problem is that, somewhat barbaric, "method A and Method B have the same erasure".
What makes it a complicated question is that we generally don't (at least I did not this very morning) know a lot about "Type Erasure".
To make it short :
Parametric types perform type check at compile time (to ensure type correctness) but forget their type parameters at runtime (to avoid the generation of underlying methods).
This sounds at the same time simple and puzzling.
Best way to understand it is to refer to the following literature :
What is a reifiable type ?
How and under what conditions is erasure performed ?
Have you any idea/examples about what it could imply in my coding life ?
Well that's odd and I don't really like it but I'm curious why they did that ...
Hope that'll help you as much as it helped me.
Specific answer :
In your case
public abstract class ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassA> list) {
//do stuff
}
}
public abstract class ClassB extends ClassA {
public static List<Sensor> createSensors(Collection<? extends ClassB> list) {
//do other stuff
}
}
will be "transformed" by javac to
public abstract class ClassA {
public static List createSensors(Collection list) {
//do stuff
}
}
public abstract class ClassB extends ClassA {
public static List createSensors(Collection list) {
//do other stuff
}
}
where one clearly can't override the other (not the same type parameter) but end up being exactly the same at runtime (no way for your program to choose which one to use).
Enough of this problem, how to solve it ?
You may proceed with one of the following approach :
Use different names : createASensors and createBSensors
this approach is the most obvious but would seem a little less elegant.
Add a parameter : createSensors(Collection<? extends ClassA> list, ClassA typeDefiner)
this approach can seem barbaric but is a little less elegant but is the one used in java.util.List for the method <T> T[] toArray(T[] a).
The general solution is to use different names. These methods could be in classes without an inheritance relationship as these are not instance methods.
As pointed out, the method implementation in the question are the same (typo excepted).
(This issue with overloading is often confused with erasure of runtime types. Overloading is a link-time rather than a dynamic issue, so could be easily fixed in the language. It's just not a particularly useful change, and not a good idea to encourage overloading.)
Check the project setting and compiler version of the project. Right click on project --> Properties --> Java Compiler. Make sure compliance setting are up to date. I had this problem when compliance settings were set to 1.4 instead 1.6

How to force a generic type parameter to be an interface?

Is there a way in java to specify, that the type parameter of a generic class must be an interface (not just extending it!)
What I want to do is the following:
public class MyClass<X extends SomeInterface, Y extends SomeOtherClass & X>
Meaning that Y must be a subclass of SomeOtherClass AND implement X.
What I currently get by the compiler is
The type X is not an interface; it cannot be specified as a bounded parameter
So, how can I tell the compiler that X must always be an interface?
Edit:
OK, I guess I oversimplified my problem a bit. Let's use my actual application domain to make it more clear:
I have an API for representing diagrams. A Diagram contains Node and Edge objects. All these three classes implement the Shape interface. Shapes may have child shapes, a parent shape and belong to a diagram.
The thing is, that I need to make two versions of this API: one open-source with just basic functionality and an extended one with more features. However, the extended API must only provide methods, which return the extended types (ExtendedDiagram, ExtendedNode, ExtendedEdge and (here comes the problem) ExtendedShape).
So I have something like this:
/* BASIC CLASSES */
public interface Shape<X extends Shape<X,Y>, Y extends Diagram<X,Y>>{
public List<X> getChildShapes();
public X getParent();
public Y getDiagram();
...
}
public class Diagram<X extends Shape<X,Y>, Y extends Diagram<X,Y>> implements Shape<X,Y>{...}
public class Edge<X extends Shape<X,Y>, Y extends Diagram<X,Y>> implements Shape<X,Y>{...}
...
/* EXTENDED CLASSES */
public interface ExtendedShape extends Shape<ExtendedShape,ExtendedDiagram> { ... }
public class ExtendedDiagram extends Diagram<ExtendedShape,ExtenedDiagram> implements ExtendedShape { ... }
public class ExtendedEdge extends Edge<ExtendedShape,ExtenedDiagram> implements ExtendedShape { ... }
...
The extended API works fine and the basic API code gives some warnings, but the main problem occurs when using the basic API:
public class SomeImporter<X extends Shape<X,Y>, Y extends Diagram<X,Y>, E extends Edge<X,Y>>{
private Y diagram;
public void addNewEdge(E newEdge){
diagram.addChildShape(newEdge);
...
That last line gives me the following warning:
The method addChildShape(X) in the type Diagram is not applicable for the arguments (E)
So now, I would just like to specify that E also needs to implement X and all would be fine - I hope ;)
Does all that make sense? Do you guys know a way to do that? Or is there even a better way to get the extended API with the said restrictions?
Thanks for sticking with me, any help is greatly appreciated!
You can use:
class Foo<T extends Number & Comparable> {...}
A class Foo with one type parameter, T. Foo must be instantiated with a type that is a subtype of Number and that implements Comparable.
In the generics context, <Type extends IInterface> handles both extends and implements. Here's an example:
public class GenericsTest<S extends Runnable> {
public static void main(String[] args) {
GenericsTest<GT> t = new GenericsTest<GT>();
GenericsTest<GT2> t2 = new GenericsTest<GT>();
}
}
class GT implements Runnable{
public void run() {
}
}
class GT2 {
}
GenericsTest will accept GT because it implements Runnable. GT2 does not, therefore it fails when trying to compile that second GenericsTest instantiation.
Maybe you can simplify your model a bit: too much generics become quickly a real pain in terms of readability, and that's quite an issue if you define a public API. Usually, if you can't understand anymore what should be inside the brackets, then you're going too far for your need - and you can't expect users to understand it better than yourself...
Anyway, in order to make your code compile, you may try defining something like this, in the Shape type:
public <S extends Shape<?,?>> void addChildShape(S shape);
That should do it.
HTH
You wrote the following:
public interface Shape<X extends Shape<X,Y>, Y extends Diagram<X,Y>>{
public List<X> getChildShapes();
public X getParent();
public Y getDiagram();
...
}
I would advise, at the minimum, getting rid of the X type variable, as follows:
public interface Shape<Y>{
public List<Shape<Y>> getChildShapes();
public Shape<Y> getParent();
public Diagram<Y> getDiagram();
...
}
The reason being is that what you originally wrote suffers from a potentially unbounded recursive nesting of the type parameters. A shape may be nested within a parent shape, which may be nested within another, all of which must be accounted for in the type signature... not a good recipe for readability. Well, it doesn't happen quite that way in your example, in which you declare "Shape<X>" instead of "Shape<Shape<X>>" but that's the direction you're going in, if you ever wanted to actually use Shape on its own...
I would probably also recommend going one step further and getting rid of the Y variable for similar reasons. Java generics don't cope very well with this sort of composition. When attempting to enforce static types for this type of modelling via generics, I've found that the type system starts to break down when you start to extend things later on.
It's typical of the Animal/Dog problem... Animal has a getChildren(), but Dog's children must also be Dogs... Java doesn't cope with this well because (in part due to the lack of abstract types as in languages like Scala, but I'm not saying you should rush off and use Scala for this problem either) the type variables have to start being declared in all sorts of places where they don't really belong.
Use a pre-processor to generate the "reduced" version of your code. Using apt and annotations might be a nice way to do it.
I might be WAY off base here, but my understanding of generics is a little different.
I am asking someone to correct me if I am wrong.
IMO -
This is a very confusing structure that you have. You have SubClasses of Shape being referenced infinitely it looks like.
Your Shape interface is utilized in the same manner as a HashMap, but I have never seen a HashMap do what you are trying to do, eventually you have to have X be a class in Shape. Otherwise you are doing HashMap
If you always want X to be a "IS A" relationship to an interface it won't happen. That is not what generics are for. Generics are used to apply methods to multiple Objects, and interfaces cannot be Objects. Interfaces define a contract between a client and a class. All you can do with the is say that you will accept any Object that implements Runnable, because all or some of your methods are required to utilize the Runnable interface methods. Otherwise if you don't specify and you define as , then your contract between your class with the client can produce unexpected behavior and cause either the wrong return value or an exception to be thrown.
For example:
public interface Animal {
void eat();
void speak();
}
public interface Dog extends Animal {
void scratch();
void sniff();
}
public interface Cat extends Animal {
void sleep();
void stretch();
}
public GoldenRetriever implements Dog {
public GoldenRetriever() { }
void eat() {
System.out.println("I like my Kibbles!");
}
void speak() {
System.out.println("Rufff!");
}
void scratch() {
System.out.println("I hate this collar.");
}
void sniff() {
System.out.println("Ummmm?");
}
}
public Tiger implements Cat {
public Tiger() { }
void eat() {
System.out.println("This meat is tasty.");
}
void speak() {
System.out.println("Roar!");
}
void sleep() {
System.out.println("Yawn.");
}
void stretch() {
System.out.println("Mmmmmm.");
}
}
Now if you did this class you can expect that you CAN always call 'speak()' & 'sniff()'
public class Kingdom<X extends Dog> {
public Kingdom(X dog) {
dog.toString();
dog.speak();
dog.sniff();
}
}
However, if you did this you CANNOT ALWAYS call 'speak()' & 'sniff()'
public class Kingdom<X> {
public Kingdom(X object) {
object.toString();
object.speak();
object.sniff();
}
}
CONCLUSION:
Generics give you the ability to utilize methods on a wide range of objects, not interfaces. Your final entry into a generic MUST be a type of Object.
The reserved word “extends” as along with a type parameter T is used to specify a bound.
‘…in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).’
[ https://docs.oracle.com/javase/tutorial/java/generics/bounded.html ]
In short, “extends” can only be used to specify a bound (whether a class or an interface) for some class type parameter T and not any interface type parameter T.
In your case,
public class MyClass<X extends SomeInterface, Y extends SomeOtherClass & X>
The compiler resolves X to be a class. For the second occurrence of X along with the type parameter Y (which clearly needs to be a class anyway), it requires X to be an interface. Since it has already resolved X to be class, it signals the error for the second occurrence of X,
The type X is not an interface;
Further, had X been specified in the first occurrence as an unbounded parameter, the compiler would have resolved it to be either a class or an interface and it would’ve considered the second occurrence of X to be a possible interface and thus allowed compilation. Since it was not so, the compiler clarifies,
it cannot be specified as a bounded parameter

Categories

Resources