Java generics: Generic parameter defined at method level vs interface level - java

I'm having the following issue to do with generics. I have the following:
InterfaceA as:
public interface InterfaceA {
public <T extends DTOInterface> Object methodName (T dto) {}
}
DTOInterface is just an empty interface.
Then my implementation would be:
public class ImplementationA implements InterfaceA {
public Object methodName(DTOImplementation dto) {
return null;
}
}
DTOImplementation is just a class implementing DTOInterface.
This is failing because the method in ImplementationA is not recognized as a valid implementation of the method in InterfaceA.
However, if I define the parameter at interface level i.e.
public interface InterfaceA **<T extends DTOInterface>** {
public Object methodName (T dto) {}
}
And then define the implementation as:
public class ImplementationA implements **InterfaceA<DTOImplementation>** {
public Object methodName(DTOImplementation dto) {
return null;
}
}
It does work. The method is recognized as a valid implementation.
Does anyone know why this is happening?

The first declaration says that in order to implement InterfaceA, the subclass needs to provide a method methodName that works for any type T that extends DTOInterface of the method caller's choice. In other words, T is a parameter that the caller of methodName gets to choose; the class that implements InterfaceA doesn't get to choose it. So when you provide an implementation that attempts to choose a particular value of T for methodName and only implement that, the compiler rejects your program.
The second declaration, on the other hand, is an interface that allows the implementor to provide a particular value for T and only implement its methods for that particular choice. ImplementationA choose to implement InterfaceA only for one particular subtype of DTOInterface (namely, DTOImplementation) and provides a method only for that choice of T. That's perfectly fine.

Your interface definition has a generic method
public <T extends DTOInterface> Object methodName (T dto) {}
This method states that it takes any type T that extends DTOInterface. If you want to implement the interface you need to provide the exact same method in the implementation.
You cannot restrict the method in an implementation because what would happen if you did this:
AnotherDTOImplementation adi = new AnotherDTOImplementation();
InterfaceA obj = new ImplementationA();
ojb.methodName(adi);
This obviously breaks type safety.
In your second example you have a generic interface. This means that when you implement the interface you either have to declare the generic type of specify the implementation as a generic class.
This means that ImplementationA is of type InterfaceA<DTOImplementation> which in turn means that you have type safety.

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.

EnumSet as a parameter in generic Interface

I've a use case :
inteface A{
get(EnumSet<?> fetchModes);
}
class B implements A{
//Here FetchMode is an enum
get(EnumSet<FetchMode> fetchMode){
//Some logic here
}
}
But it's throwing compile time error :
Method get of class B has the same erasure as get(EnumSet fetchMode) of type A but doesn't override it.
I had read that Enums cannot be generic but is there any way to implement this usecase ?
(Basically want the EnumSet to be generic and different implementations can pass different Enums)
A method can override another if the argument types match exactly, but yours doesn't. Eventhough EnumSet<FetchMode> is a subtype of EnumSet<?>, they are not exactly the same. You are not overriding the super class method get(EnumSet<?> fetchModes); in your subclass, rather you are overloading it with a different parameter type. Both of these has the same signature due to erasure when you inspect or decompile the bytecode which is get(EnumSet fetchModes) and your compiler starts complaining.
This is covered in JLS ยง8.4.8.1:
A class cannot have two member methods with the same name and type
erasure
A naive attempt at fixing the problem is to change the parameter type such that it is compatible with that of the super class, overriding the method properly in your sub class.
#Override
public void get(EnumSet<?> fetchModes) {
}
Though this fixes the compiler error after a fashion, it is still not elegant since it allows your EnumSet to store any Object. But ideally you may want it to store only some subtype of Enum. This idiom supports that.
What you have to do is declare a generic interface with bounded type parameter and then implement it by overriding the method properly as shown below.
public interface A<E extends Enum<E>> {
void get(EnumSet<E> fetchModes);
}
public class B implements A<FetchMode> {
#Override
public void get(EnumSet<FetchMode> fetchModes) {
}
}
try this you have to make the generic type extends Enum:
public class B implements A<FetchMode> {
//Here FetchMode is an enum
public void get(EnumSet<FetchMode> fetchMode){
//Some logic here
}
}
}
interface A<T extends Enum<T>> {
void get(EnumSet<T> fetchModes);
}

Passing Class objects in java

I have an Interface and 2 concrete class that implement that interface,
public interface ITemplate{}
public Template implements ITemoplate {}
public Template2 implements ITemplate {}
I have a method that takes in the Class object and instantiates it.
public addTemplate(Class<ITemplate> template){
pipe.add(template.newInstance())
}
The problem is that when I call that method, it throws a compile time error:
instance.addTemplate(Template.class)
Compile Time Error :
addTemplate(java.package.ITemplate.class) cannot be applied to addTemplate(java.package.Template.class)
Am I missing something, or is there a work around for this?
Class<ITemplate> will strictly accept the ITemplate.class
Class<? extends ITemplate> will accept any of the classes implementing ITemplate.class
try this method:
// works for all classes that inherit from ITemplate.
public addTemplate(Class< ? extends ITemplate> template){
pipe.add(template.newInstance())
}
instead of
// only accepts ITemplate Type Class (Strict Type).
public addTemplate(Class<ITemplate> template){
pipe.add(template.newInstance())
}
Here is an explanation: when you use Class<ITemplate> it is a strict type of class Itemplate. It will never take any other type argument other than ITemplate, because it is resolved at compile time only.
However Class <? extends ITemplate> can accept all objects that are either ITemplate or have ITemplate as a superclass.

Java Generics - Cannot override a method with an extends generic

I have this method in a service AbstractClass:
public <E extends EntityFilter> Specification<T> getSpecifications(E entityFilter) {
return null;
}
Then I have an implementation for EntityFilter too:
public class UserEvaluationFilter implements EntityFilter {
#Getter
#Setter
private String evaluator;
}
And I want to override the AbstractClass in my serviceClass (which extends the controller AbstractClass) method like this:
#Override
public Specification<UserEvaluation> getSpecifications(UserEvaluationFilter filter) {
return doStuff();
}
The compiler says that this is not overriding any method of my AbstractClass.
What's wrong?
The method signature you have declared in the abstrct class says that the method should accept any subclass of EntityFilter as that parameter.
Actually, the type variable is redundant there: you may as well just declare it as:
public Specification<T> getSpecification(EntityFilter entityFilter)
What you're trying to do in your subclasses is to make the parameter type more specific than EntityFilter; but this is forbidden by the Liskov Subtitution Principle, which says that subclasses must be:
No more specific in the parameters they accept;
No more general in the values they return.
As such, the method you are trying to declare in the subclass doesn't actually override the method in the supertype, so it is forbidden.
To deal with this, you need to make the filter type a class-level type variable:
class AbstractClass<T, E extends EntityFilter> {
public Specification<T> getSpecifications(E entityFilter) {
return null;
}
}

declare parameter subtype in Java interface, use subtypes in Java implementing methods

I want to declare a method in an interface where the parameter of the method defined in implementing classes can be a subtype of a specific java class for example:
interface Processor{
processRequest( Request r);
}
public class SpecialRequest extends Request{...}
public class SpecialProcessor implements Processor{
processRequest(SpecialRequest r){...}
}
but I get errors in the SpecialProcessor because it doesn't properly implement the Processor interface. What can I change in the Processor interface to allow the definition in the SpecialProcessor to work?
You can type Processor:
public interface Processor<R extends Request> {
void processRequest(R r);
}
public class SpecialProcessor implements Processor<SpecialRequest> {
public void processRequest(SpecialRequest r) {
...
}
}
That's right - remember that a caller shouldn't know what specific implementation of the interface is being used. They just know that they can pass a Request (any Request) to processRequest, whereas your implementation is imposing a stricter constraint on the argument that would cause certain method calls not to be type-correct.
If you want to do this, you'll need to add a generic parameter to the interface, something like the following:
interface Processor<R extends Request> {
void processRequest(R r);
}
public class SpecialProcessor implements Processor<SpecialRequest> {
public void processRequest(SpecialRequest r) { ... }
}
This way, callers that want to pass in "normal" requests will have to declare a variable/field of type Processor<Request> - and your SpecialProcessor no longer matches this bound, so cannot be assigned, and will correctly be rejected at compile-time. Callers that are dealing with special requests themselves can use a Processor<SpecialRequest> variable/field, which your class can be assigned to.

Categories

Resources