I'd like to create a generic enum-based mapper for IBatis. I'm doing this with the below code. This does have compile time errors, which I don't know how to fix. Maybe my solution is just plain wrong (keep in mind the use of IBatis), in such case please suggest something better.
Any help appreciated.
What I want to achieve is to define subsequent mappers as:
public class XEnumTypeHandler extends CommonEnumTypeHandler<X> {
}
The current code:
public class CommonEnumTypeHandler<T extends Enum> implements TypeHandlerCallback {
public void setParameter(ParameterSetter ps, Object o) throws SQLException {
if (o.getClass().isAssignableFrom(**T**)) {
ps.setString(((**T**) o).value().toUpperCase());
} else
throw new SQLException("Excpected ParameterType object than: " + o);
}
public Object getResult(ResultGetter rs) throws SQLException {
Object o = valueOf(rs.getString());
if (o == null)
throw new SQLException("Unknown parameter type: " + rs.getString());
return o;
}
public Object valueOf(String s) {
for (T pt : T.**values()**) {
if (pt.**value()**.equalsIgnoreCase(s))
return pt;
}
return null;
}
}
I've added error markings to the above code, the error messages are in order:
T cannot be resolved
The method value() is undefined for
the type T
The method values() is undefined for
the type T
The method values() is undefined for
the type T
I've solved this issue with the following code:
public class CommonEnumTypeHandler<T extends Enum> implements TypeHandlerCallback {
Class<T> clazz;
public CommonEnumTypeHandler(Class<T> clazz) {
this.clazz = clazz;
}
public void setParameter(ParameterSetter ps, Object o) throws SQLException {
if (o.getClass().isAssignableFrom(clazz)) {
ps.setString(((T) o).name().toUpperCase());
} else
throw new SQLException("Excpected " + clazz + " object than: " + o);
}
public Object getResult(ResultGetter rs) throws SQLException {
Object o = valueOf(rs.getString());
if (o == null)
throw new SQLException("Unknown parameter type: " + rs.getString());
return o;
}
public Object valueOf(String s) {
return Enum.valueOf(clazz, s);
}
}
Inheriting from this class I do:
public class SalesChannelTypeHandler extends CommonEnumTypeHandler<SalesChannel> {
public SalesChannelTypeHandler() {
super(SalesChannel.class);
}
public SalesChannelTypeHandler(Class<SalesChannel> clazz) {
super(clazz);
}
}
I'm not sure what you're doing (a general overview in words would be nice), but:
You can't do isAssignableFrom(T) (you need a Class object), and you can't do instanceof T either (generics are non-reified). You may want to pass Class<T> type tokens instead.
Have you looked at EnumMap?
See also
Java Tutorials/Runtime Type Tokens
Neal Gafter's Blog - Super Type Tokens
Josh Bloch - Typesafe Heterogenous Container (THC) pattern (PDF)
It's still not clear what is desired, but perhaps it's something along the lines of this:
enum Color { BLACK, WHITE; }
public static void main(String[] args) {
Color c = Enum.valueOf(Color.class, "black".toUpperCase());
System.out.println(c); // prints "BLACK"
}
So we use Enum.valueOf that takes a type token Class<T extends Enum<T>>, and ask it for the enum constant with a given name. valueOf is NOT case-insensitive, but by conventions, all constants should be in uppercase, so we simply take the query string and turn it .toUpperCase().
As pointed by Polygenelubricants, you need to pass concrete runtime types around, e.g. Class<?> instead of syntactic compiletime types like generic parameters. Here's a rewrite how you could use it:
public abstract class CommonEnumTypeHandler<E extends Enum<E>> implements TypeHandlerCallback {
private Class<E> enumClass;
public CommonEnumTypeHandler(Class<E> enumClass) {
this.enumClass = enumClass;
}
public void setParameter(ParameterSetter ps, Object o) throws SQLException {
if (enumClass.isInstance(o)) {
ps.setString((enumClass.cast(o)).name().toUpperCase());
} else {
throw new SQLException("Excpected ParameterType object than: " + o);
}
}
public Object getResult(ResultGetter rs) throws SQLException {
try {
return Enum.valueOf(enumClass, rs.getString());
} catch (IllegalArgumentException e) {
throw new SQLException("Unknown parameter type: " + rs.getString(), e);
}
}
}
Which you can then use as follows:
public class XEnumTypeHandler extends CommonEnumTypeHandler<X> {
public XEnumTypeHandler() {
super(X.class);
}
}
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
Let's say I have a class,
public class Dummy {
public List<Integer> foo() {...}
public Abc<Integer> bar() {...}
}
For method foo, I can check the return type using ParameterizedType, but for method bar, is there a way to know if Ab<Integer> is actually a List<Integer>?
Edit to add some background:
We have dozens of 3rd party POJO classes, and we need to extract the field information of these classes, and create a schema based on it.
For the regular primitive fields, it's straight forward, but in those classes, they also have field with type like List<Integer>, or ArrayList<Integer>, or Abc<Integer> (Abc<T> may or may not implements List<T>).
So we need a way to determine if it's a repeated field or not, if it's a repeated one, what is the type argument, is it an Integer or some other things?
You can use Class.isAssignableFrom to determine whether a particular class IS-A another class, and you can look at the type arguments given to the return type:
public class App {
static class AbcWhichIsAList<E> extends ArrayList<E> {
}
static class AbcWhichIsNotAList<E> {
}
public List<Integer> foo() { return null; }
public AbcWhichIsAList<Integer> bar() { return null; }
public AbcWhichIsNotAList<Integer> baz() { return null; }
public AbcWhichIsAList<String> xyzzy() { return null; }
public static boolean returnsList(Class clazz, String methodName) throws NoSuchMethodException {
Method m = clazz.getMethod(methodName);
return List.class.isAssignableFrom(m.getReturnType());
}
public static boolean returnsIntegerList(Class clazz, String methodName) throws NoSuchMethodException {
Method m = clazz.getMethod(methodName);
Type returnType = m.getGenericReturnType();
if (returnType instanceof ParameterizedType parameterisedReturnType) {
return List.class.isAssignableFrom(m.getReturnType()) &&
parameterisedReturnType.getActualTypeArguments()[0].getTypeName().equals(Integer.class.getTypeName());
} else {
return false;
}
}
public static void main(String[] args) throws NoSuchMethodException {
for (String s: Arrays.asList("foo", "bar", "baz", "xyzzy") ){
System.out.println(s + ": " + returnsIntegerList(App.class, s));
}
}
}
The above code assumes that the methods of interest take no parameters.
I feel that there should be a better way of comparing the type parameters than comparing the names of the types.
It's possible to handle more complicated cases. I don't have time to write a full solution, but the code below does half the job: It traverses the inheritance hierarchy until it finds List. However it does not keep track of the actual type parameters on the way.
package org.example;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class App {
static class AbcWhichIsAList<E> extends ArrayList<E> {}
static class AList extends AbcWhichIsAList<Integer> {}
static class AbcWhichIsNotAList<E> {}
public List<Integer> foo() { return null; }
public AbcWhichIsAList<Integer> bar() { return null; }
public AbcWhichIsNotAList<Integer> baz() { return null; }
public AbcWhichIsAList<String> xyzzy() { return null; }
public AList blech() { return null; }
public static boolean returnsIntegerList(Class clazz, String methodName) throws NoSuchMethodException {
Method m = clazz.getMethod(methodName);
Type returnType = findListType(m.getGenericReturnType());
if (returnType != null && returnType instanceof ParameterizedType parameterisedReturnType) {
return List.class.isAssignableFrom(m.getReturnType()) &&
parameterisedReturnType.getActualTypeArguments()[0].getTypeName().equals(Integer.class.getTypeName());
} else {
return false;
}
}
private static Type findListType(Type type) {
if (type.getTypeName().startsWith("java.util.List")) {
return type;
}
Type listType = null;
if (type instanceof ParameterizedType parameterizedType) {
listType = findListType(parameterizedType.getRawType());
}
if (type instanceof Class clazz) {
if (clazz.getGenericSuperclass() != null) {
listType = findListType(clazz.getGenericSuperclass());
}
if (listType == null) {
for (Type t : clazz.getGenericInterfaces()) {
listType = findListType(t);
if (listType != null)
break;
}
}
}
return listType;
}
public static void main(String[] args) throws NoSuchMethodException {
for (String s: Arrays.asList("foo", "bar", "baz", "xyzzy", "blech") ){
System.out.println(s + ": " + returnsIntegerList(App.class, s));
}
}
}
Read about the object.instanceof(someType) method, it might be what you are looking for.
The java instanceof operator is used to test whether the object is an instance of the specified type (class or subclass or interface).
If not - the typeof method is worth trying as well.
I have tried:
package ro.ex;
import java.io.IOException;
import java.lang.reflect.Type;
class Ex {
public boolean isIns(Object o, Class t) {
o instanceof t;
}
public static void main(String[] args) throws IOException {}
}
above code will raise unknown class "t"
My question is: How to pass above code.
update:
following code can't pass intellij idea syntax checker
public boolean isIns(Object o, Class<?> t) {
return o instanceof t;
}
so the right code in idea is:
public boolean isIns(Object o, Class<?> t) {
return t.isAssignableFrom(o.getClass());
}
the more simple way is:
package ro.ex;
import java.io.IOException;
import java.lang.reflect.Type;
class Ex {
public boolean isIns(Object o, Class t) {
return t.isInstance(o);
}
public static void main(String[] args) throws IOException {
Object r = new Ex().isIns("", String.class);
System.out.println(r + "\t\t" + new Exception().getStackTrace()[0].getFileName() + ":" + new Exception().getStackTrace()[0].getLineNumber());
}
}
If you write x instanceof t, then t must be a class. In your isIns method, t is not a class, it is a variable of type Class.
The class Class, however, does offer methods with which you can decide whether some other class is a subclass of it: Class.isAssignableFrom(Class). So you can change you method to:
public boolean isIns(Object o, Class t)
{
return t.isAssignableFrom(o.getClass());
}
(I also changed your code so that the result of is returned to the caller.)
I have no idea what you are trying to do with your method, but the instanceof syntax is wrong.
public boolean isIns(Object o, Class t) {
return o instanceof t;
}
instanceof keyword checks with a Valid class name not with variable name. That's the compiler error.
For ex : o instanceof String, if you write like below, it won't compile
public boolean isIns(Object o, String str) {
return o instanceof str; //err, o instance of String is correct way to check.
}
And I slightly changed your method signature to match the statement.
You should use the dinamic equivalent of instanceof from Class class.
http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#isInstance(java.lang.Object)
public void isIns(Object o, Class t) {
t.isInstance(o);
}
Check for nulls if needed.
In your code below, t should be the name of a Java class (e.g. String). In your code, you've passed a variable name which is not appropriate:
public void isIns(Object o, Class t) {
o instanceof t;
}
Since an instanceof check is a one-liner, I'm not sure why you are wrapping it in a method. But if you insist on doing so, perhaps this is what you want:
public static boolean isIns(Object o, Class<?> t) {
return c.isInstance(o);
}
But two notes:
Lots of instanceof checks generally indicate bad design.
A Java programmer will be much more comfortable seeing instanceof rather than isIns(...).
maybe you want something like this?
public class test2 {
class c {
}
public test2() {
c obj = new c();
System.out.println(isIns(obj));
}
public boolean isIns(Object o) {
return (o instanceof c);
}
public static void main(String argv[]) {
new test2();
}
}
EDIT: Removed senceless Class parameters.
I have this (simplified) java interface
public interface MyInterface<T> {
public String run( T arg );
}
and some classes that implement that interface, i.e.
public final class SomeImplementation1 implements MyInterface<String> {
#Override
public String run( String arg) {
// do something with arg and return a string
}
}
and
public final class SomeImplementation2 implements MyInterface<CustomClass> {
#Override
public String run( CustomClass arg) {
// do something with arg and return a string
}
}
Now, I have a global resource manager for all of those implementations, which instantiates all of them in a List for latter usage. What I would like to achieve is something like this, which obviously gives me an error
public final class MyInterfaceManager {
private List<MyInterface<?>> elements = new List<MyInterface<?>>();
public MyInterfaceManager() {
elements.put( new SomeImplementation1() );
elements.put( new SomeImplementation2() );
// more implementations added
}
// this is what I would like to achieve
public <T> void run( T arg ) {
for( MyInterface<?> element: elements ) {
String res = element.run( arg ); // ERROR
}
}
}
because "arg cannot be converted to capture#1 of ? by method invocation conversion".
A possible solution could be to perform an instanceof test inside the loop, and cast the element to its real type, along with the argument as well, like that
public <T> void run( T arg ) {
for( MyInterface<T> element: elements ) {
if (element instanceof SomeImplementation2) {
String res = ((SomeImplementation2)element).run( (CustomClass)arg );
} else if // other tests here ...
}
}
But i don't like it, it's not elegant at all, and it forces me to do lots of instanceof and casts.
So, I'm wondering if there is a better way to achieve this.
Thanks for your help :)
You are running into type erasure. You need to add another method to the interface that returns the Class instance that relates to the type parameter <T>, this will allow you to do runtime checks on that Class.
I would accomplish this thus:
public interface MyInterface<T> {
String run( T arg );
Class<T> type();
}
So the interface returns its type. N.B. all interface members are public by default - no need for the extra public.
public final class SomeImplementation1 implements MyInterface<String> {
#Override
public String run(final String arg) {
return arg;
}
#Override
public Class<String> type() {
return String.class;
}
}
#SuppressWarnings({"unchecked"})
public static <T> String run(final T arg) {
for (final MyInterface<?> element : elements) {
if (element.type().isAssignableFrom(arg.getClass())) {
return ((MyInterface<T>) element).run(arg);
}
}
throw new IllegalArgumentException("No element found.");
}
The logic is that for each MyInterface you check whether the argument provided is safely castable to that MyInterface's type(). If it is then you can cast the whole MyInterface to the arg's type. This is unchecked as the compiler cannot verify this as compile time, but as you are manually doing a check this warning can be ignored.
public static void main(String[] args) throws Exception {
elements = new LinkedList<>();
elements.add(new SomeImplementation1());
System.out.println(run("test"));
System.out.println(run(1));
}
Output:
test
Exception in thread "main" java.lang.IllegalArgumentException: No element found.
at com.XXX.App.run(App.java:33)
at com.XXX.App.main(App.java:55)
As expected.
I have an third-party RPC-API that provides an interface similar to that of java.sql.ResultSet (for reading values) and java.sql.PreparedStatement (for writing values). Assume it looks something like this:
public interface RemoteDeviceProxy {
public void setBoolean(Boolean value);
public void setInteger(Integer value);
// ...
public Boolean getBoolean();
public Integer getInteger();
// ...
}
I want to write a wrapper for this API that uses generics to create instances of specific types:
public class <T> RemoteVariable {
private final RemoteDeviceProxy wrappedDevice;
public RemoteVariable(RemoteDeviceProxy wrappedDevice) {
this.wrappedDevice = wrappedDevice;
}
public T get() {
// should call wrappedDevice.getBoolean() if T is Boolean, etc.
// how to implement?
}
public void set(T newValue) {
// should call wrappedDevice.setBoolean(newValue) if T is Boolean, etc.
// implement using instanceof
}
}
How can I implement the getter in my generic wrapper? I have found this answer which explains a similar scenario in depth, but I am not able to transfer this to my problem. Specifically, when I write this:
public T get() {
Type[] actualTypeArguments = ((ParameterizedType) getClass())
.getActualTypeArguments();
}
I get a compiler error saying I cannot cast to ParameterizedType, and I do not understand why. Can anyone explain how to achieve this?
Here is one way:
public class <T> RemoteVariable {
private final RemoteDeviceProxy wrappedDevice;
private final Class<T> clazz;
public RemoteVariable(RemoteDeviceProxy wrappedDevice, Class<T> clazz) {
this.wrappedDevice = wrappedDevice;
this.clazz = clazz;
}
public T get() {
if(clazz == Boolean.class){return clazz.cast(wrappedDevice.getBoolean());}
else if(clazz == Integer.class){return clazz.cast(wrappedDevice.getInteger());}
// ...
}
// ...
}
I thought over this quite a while and finally came up with a different approach:
First I added a getter to you RemoteVariable class:
protected RemoteDeviceProxy getWrappedProxy() {
return wrappedProxy;
}
Second I created a builder interface that will be used by a factory later:
public interface RemoteVariableBuilder {
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy);
}
Then I created non generic sub classes for Boolean...
public class RemoteBooleanVariable extends RemoteVariable<Boolean> implements RemoteVariableBuilder {
public RemoteBooleanVariable(RemoteDeviceProxy wrappedProxy) {
super(wrappedProxy);
}
#SuppressWarnings("unchecked")
#Override
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy) {
return (RemoteVariable<T>) new RemoteBooleanVariable(wrappedProxy);
}
#Override
public Boolean get() {
return getWrappedProxy().getBoolean();
}
#Override
public void set(Boolean value) {
getWrappedProxy().setBoolean(value);
}
}
... and Integer ...
public class RemoteIntegerBuilder extends RemoteVariable<Integer> implements RemoteVariableBuilder {
public RemoteIntegerBuilder(RemoteDeviceProxy wrappedProxy) {
super(wrappedProxy);
}
#SuppressWarnings("unchecked")
#Override
public <T> RemoteVariable<T> buildNewVariable(RemoteDeviceProxy wrappedProxy) {
return (RemoteVariable<T>) new RemoteIntegerBuilder(wrappedProxy);
}
#Override
public Integer get() {
return getWrappedProxy().getInteger();
}
#Override
public void set(Integer value) {
getWrappedProxy().setInteger(value);
}
}
actually eclipse created most of the code once it knew base class and interface.
The final step was to create a factory
public class RemoteVariableFactory {
private static final Map<String, RemoteVariableBuilder> BUILDERS = new HashMap<>();
static {
BUILDERS.put(Boolean.class.getName(), new RemoteBooleanVariable(null));
BUILDERS.put(Integer.class.getName(), new RemoteIntegerBuilder(null));
// add more builders here
}
public static <T> RemoteVariable<T> getRemoteVariable(RemoteDeviceProxy wrappedProxy, Class<T> typeClass) {
RemoteVariableBuilder remoteVariableBuilder = BUILDERS.get(typeClass.getName());
if (remoteVariableBuilder == null) {
return null; // or throw an exception whichever is better in your case
}
return remoteVariableBuilder.buildNewVariable(wrappedProxy);
}
}
Now we are ready to create new RemoteVariables...
RemoteVariable<Boolean> var1 = RemoteVariableFactory.getRemoteVariable(new RemoteDevice(), Boolean.class);
RemoteVariable<Integer> var2 = RemoteVariableFactory.getRemoteVariable(new RemoteDevice(), Integer.class);
To conclude this let's do a quick comparison to the answer of Eng.Fouad:
Disadvantage:
you need to create a new class for every datatype you provide
Advantage:
you only have to add one line to the static block of the factory and not two new if blocks to the getter and setter in RemoteVariable
get and set do not have to work through the if-else-blocks every time
Is it possibly to access the generic argument in the following line ?
public List<StoryLikeRef> getLikes() throws IOException
I mean by reflection get the StoryLikeRef from out the return type ?
Thanks
Yes, you can, assuming StoryLikeRef is a concrete type (rather than a type parameter itself). Use Method.getGenericReturnType to get a Type. Sample code:
import java.lang.reflect.*;
import java.util.*;
public class Test {
public List<String> getStringList() {
return null;
}
public List<Integer> getIntegerList() {
return null;
}
public static void main(String[] args) throws Exception {
showTypeParameters("getStringList");
showTypeParameters("getIntegerList");
}
// Only using throws Exception for sample code. Don't do
// this in real life.
private static void showTypeParameters(String methodName)
throws Exception {
Method method = Test.class.getMethod(methodName);
Type returnType = method.getGenericReturnType();
System.out.println("Overall return type: " + returnType);
if (returnType instanceof ParameterizedType) {
ParameterizedType type = (ParameterizedType) returnType;
for (Type t: type.getActualTypeArguments()) {
System.out.println(" Type parameter: " + t);
}
} else {
System.out.println("Not a generic type");
}
}
}