Getting Enum class from generic type? - java

Suppose I have several classes:
Class ExceptionA{
public ExceptionA(ExceptionA.ErrorCode errorCode){}
setters...
getters...
public static enum ErrorCode{
EC_ABC,EC_XYZ,EC_123
}
Class ExceptionB{
public ExceptionB(ExceptionB.ErrorCode errorCode){}
setters...
getters...
public static enum ErrorCode{
EC_DEF,EC_LOL,EC_456
}
In a loop somewhere that works with an array containing ExceptionA, ExceptionB, ExceptionC objects: I want to generically construct an Exception object using its constructor without ever explicitly stating ExceptionX.ErrorCode.
Class<? extends Exception> expectedException = exception.getClass().getConstructor(Enum.class).newInstance(someErrorCodeEnum);
The issue occurs at getConstructor(). Constructors do exist for each Exception class, but they take SpecificException.ErrorCode type. Not just a generic Enum.class. Is there some method that might work like this?:
ExceptionA exceptionAobject = new ExceptionA(EC_ABC);
exceptionAobject.getEnumClassFromString("ErrorCode"); // Should be of type ExceptionA.ErrorCode

It depends on the circumstances. If you know for sure that there will be only a single constructor, you could simply call, e.g. ExceptionA.class.getConstructors()[0] to get the sole constructor. You could even call getParameterTypes()[0] on the constructor object to get the actual ErrorCode type.
Otherwise, if you know that there should be an inner class named ErrorCode, you have to use the Binary name of the inner class, i.e.
Class<? extends Exception> exceptionType = exception.getClass();
Class<?> errorCodeType = exceptionType.getClassLoader()
.loadClass(exceptionType.getName()+"$ErrorCode");
assert errorCodeType.getDeclaringClass() == exceptionType;
Then, you can lookup the constructor using
Constructor<? extends Exception> con = exceptionType.getConstructor(errorCodeType);
But maybe you are thinking too complicated. If you already have your someErrorCodeEnum object that you intend to pass to the constructor, you can simply use this object to determine the parameter type:
Constructor<? extends Exception> con = exception.getClass()
.getConstructor(((Enum<?>)someErrorCodeEnum).getDeclaringClass());
Note the importance of using Enum.getDeclaringClass() rather than Object.getClass() here, as a particular enum constant may be of an anonymous inner class extending the formal enum type. getDeclaringClass() will return the right type.

I am not quite sure I got your requirements. I am thinking this ought to be doable without reflection, so here’s my idea:
public class ExceptionA extends Exception {
public ExceptionA(ExceptionA.ErrorCode errorCode) {
}
public static enum ErrorCode implements ExceptionErrorCode {
EC_ABC, EC_XYZ, EC_123;
#Override
public Exception toException() {
return new ExceptionA(this);
}
}
}
I am using this little interface:
public interface ExceptionErrorCode {
Exception toException();
}
This will allow something like:
ExceptionErrorCode someErrorCodeEnum = ExceptionA.ErrorCode.EC_XYZ;
Exception expectedException = someErrorCodeEnum.toException();
Would this fulfil your requirements?
I am thinking for the sake of the model you may want to introduce a common superclass for your exception classes so you need not declare toException() and expectedException just Exception — it’s a vague type for my taste. And even though you don’t see the need immediately, the supertype could come in handy some time.

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);
}

Return Type in F-Bounded Polymorphism Java

I have an interface:
public interface Message<T extends Message<T>> {
}
I have a class that implements this method as
public class FulfilmentReleasedDomModel implements Message<FulfilmentReleasedDomModel> {}
And I have this method:
private <T extends Message<T>> Mono<T> getDomainModel(ConsumerRecord<String, String> record) {}
When I try to return an object of type Mono<FulfilmentReleaseDomModel> from this method, the compiler throws an error and asks me to cast it to (Mono<T>).
My question is, since I have bounded T to extends Message, and FulfilmentReleasedDomModel implements Message<FulfilmentReleasedDomModel>, why do I need to cast it to Mono<T>?
A common misconception about generics is that the callee decides what the generic type is. No, the caller does.
You, as the writer of the method, don't get to decide what T is. By returning Mono<FulfilmentReleasedDomModel>, you are saying that T must be FulfilmentReleasedDomModel. But in actuality, the caller of your method will decide what T is. They could declare a type called Foo that implements Message<Foo> and say that T is Foo. You would need to return a Mono<Foo> instead.
It seems like your method should not be generic, because the callee is deciding what type to use:
private Mono<FulfilmentReleasedDomModel> getDomainModel(ConsumerRecord<String, String> record) {}
Alternatively, if you want to make this method more flexible, so that you can change its implementation to return something else without changing its return type, you can use generic wildcards:
private Mono<? extends Message> getDomainModel(ConsumerRecord<String, String> record) {}

Java: Generic accepting only enum types

Assuming that I have a basic enum like:
public enum Color { Red, Green, Blue}
How can one write a generic class which only accepts "enum classes" so that a concrete instantiation of that generic class might look like MyClass<Color>?
Edit:
What a actually want to do is to write a generic abstract class containing a function returning all enum "entries" as list:
public abstract class EnumListBean<E extends Enum<E>> {
public List<E> getEnumList() {
return Arrays.asList(E.values());
}
}
While Day.values() is available E.values() is not. What i am doing wrong here?
public class EnumAcceptor<E extends Enum<E>> {
...
}
Use E as a type inside your class.
See Istvan Devai for answer to original question.
For the follow up, methods like values() are static methods, so you're out of luck trying to get that from a generic parameter. As a poor solution, you can pass the enum's Class object into the constructor. and use Class.getEnumConstants. But you might as well pass MyEnum.values() into the constructor rather than the class, and so avoid reflection altogether. It's a real shame there isn't a sensible enum metaclass.
An enum really declares a class derived from Enum. As such, you can use:
public class MyClass<T extends Enum> { }
Note that #Istvan's solution can only accept elements of the enum, which is fine if that is all you want.
Although you cannot pass the enum itself as a parameter (because it does not actually have an object equivalent) you can specify that you must receive the class of the enum in your constructor and derive the enum's details from that:
public class EnumAcceptor<E extends Enum<E>> {
public EnumAcceptor(Class<E> c) {
// Can get at the enum constants through the class.
E[] es = c.getEnumConstants();
}
enum ABC {
A, B, C;
}
public static void main(String args[]) {
EnumAcceptor<ABC> abcAcceptor = new EnumAcceptor<ABC>(ABC.class);
}
}
You can't use E.values() due to type erasure -- the type of E is not available at run-time.
For the specific case in your question, you're probably better off using Guava's Lists.newArrayList:
List<Color> days = Lists.newArrayList(Color.values());

Java generic methods cast to parameter type at runtime, is it possible?

I have a method that looks like this
public static <T extends MyClass, X extends AnotherClass> List<T> (Class<T> aParameter, X anotherParameter)
Now if AnotherClass is an abstract class that does NOT Have getId defined, but every single class that extends this interface does. (Don't ask me why it is designed this why, I did not design the abstract class, and I am not allowed to change it).
How can I do something like this
anotherParameter.getId();
I know I have to cast it to the class, but then i have to do an instanceof check for every possible class and then cast it.
So right know i have something like:
if (anotherParameter instanceof SomeClass)
((SomeClass)anotherParameter).getId(); //This looks bad.
Is it possible to cast this dynamically?, to whatever anotherParameter is at runtime?.
Can you modify derived classes? If so, you could define an interface for this (syntax maybe wrong):
public interface WithId {
void getId();
}
...
public class MyDerivedClass1 extends AnotherClass implements WithId {
...
}
...
public class MyDerivedClass2 extends AnotherClass implements WithId {
...
}
and then, inside your method do:
...
if (anotherParameter instanceof WithId) {
WithId withId = (WithId) anotherParameter;
withId.getId();
}
...
If you can change your method's signature, maybe you can specify an intersection type:
public static <T extends MyClass, X extends AnotherClass & WithId> List<T> myMethod(Class<T> aParameter, X anotherParameter)
and then you would have getId() available directly inside your method.
I would say no, since due to type erasure, X is actually just Object at runtime. You could try doing something with reflection to test if anotherParameter has getId() and if so, call it.
You could use reflection to invoke the method at runtime if it exists.
try {
Method m = anotherParameter.getClass().getMethod("getId", null);
Object result = m.invoke(anotherParameter, null);
}
catch (NoSuchMethodException e) {
// ... and several other exceptions need to be caught
}
The concept of casting something at runtime doens't really make sense, as you have an instance, and it can tell you what class it is. You will need to use reflection, for example using the Introspector class.
private Integer getId(final X anotherParameter) {
final BeanInfo beanInfo = Introspector.getBeanInfo(anotherParameter.getClass());
for (MethodDescriptor methodDescriptor : beanInfo.getMethodDescriptors()) {
final Method method = methodDescriptor.getMethod();
if ("getId".equals(method.getName())
&& method.getParameterTypes().length == 0) {
return (Integer) method.invoke(anotherParameter);
}
}
return null;
}
As others here have said, reflection is the only practical solution to this, but I would enhance this a little bit by caching the reflection metadata (perhaps a map keyed by the class+methodName), as that part of reflection isn't completely cheap. You can't help the "invoke()" part.

Categories

Resources