I now came several times across this problem and always kinda solved this with some casts and #SuppressWarnings annotations.
The relevant interfaces / abstract classes:
public abstract class Data { }
public interface DataOperations {
boolean isValid();
}
public interface DataOperationsFactory<T extends Data> {
Class<T> getDataClass();
DataOperations getOperations(T data);
}
Example Implementations:
public class DataImpl1 extends Data {
public String foo;
}
public class DataImpl1Operations implements DataOperations {
private DataImpl1 data;
public DataImpl1Operations(DataImpl1 data) {
this.data = data;
}
public boolean isValid() {
return data.foo != null;
}
}
public class DataImpl1OperationsFactory extends DataOperationsFactory<DataImpl1> {
public Class<DataImpl1> getDataClass() {
return DataImpl1.class;
}
DataOperations getOperations(DataImpl1 data) {
return new DataImpl1Operations(data);
}
}
Using this pattern, I can decide whether I need to create a new DataImpl1Operations everytime. Or maybe use a final static NO_OP implementation or what have you.
The Code:
Now I'd like to put all those factories inside a Map<Class<T>, DataOperationsFactory<T>> (constructor). And afterwards read from it (getOps method).
public class Test {
Map<Class<?>, DataOperationsFactory<?>> map;
public Test(List<DataOperationsFactory<?>> fs) {
for(DataOperationsFactory<?> f : fs) {
map.put(f.getDataClass(), f);
}
}
#SuppressWarnings("unchecked")
public <T extends Data> DataOperations getOps(T data) {
// --> Here I need to do an unchecked cast <--
DataOperationsFactory<? super T> f =
(DataOperationsFactory<? super T>) map.get(data.getClass());
return f.getOperations(data);
}
}
Is there any way doing this without unchecked casting?
You can delegate to a private method that captures the type, so it can be used to reliably cast to the correct Data subclass:
Map<Class<?>, DataOperationsFactory<?>> map;
// Unchanged
public Test(List<DataOperationsFactory<?>> fs) {
for(DataOperationsFactory<?> f : fs) {
map.put(f.getDataClass(), f);
}
}
public DataOperations getOps(Data data) {
DataOperationsFactory<?> f = map.get(data.getClass());
return getOperations(f, data);
}
private static <T extends Data> DataOperations getOperations(DataOperationsFactory<T> f,
Data data) {
return f.getOperations(f.getDataClass().cast(data));
}
Related
I have the following classes
public class PathPart {
private final String pathPart;
public PathPart(String pathPart) {
this.pathPart = pathPart;
}
public String getPathPart() {
return pathPart;
}
}
public class Path {
private final List<? extends PathPart> pathParts;
public Path(String path) {
this.pathParts = getPathParts(path);
}
public List<? extends PathPart> getPathParts() {
return this.pathParts;
}
protected List<? extends PathPart> getPathParts(String path) {
return Arrays.stream(path.split("/")).map(PathPart::new).collect(Collectors.toList());
}
}
public class FooBarPathPart extends PathPart {
public FooBarPathPart(String pathPart) {
super(isFoo(pathPart) ? "bar" : pathPart);
}
private static boolean isFoo(String pathPart) {
return "foo".equals(pathPart);
}
}
public class FooBarPath extends Path {
public FooBarPath(String path) {
super(path);
}
#Override
public List<FooBarPathPart> getPathParts() {
// UNCHECKED CAST HERE
return (List<FooBarPathPart>) super.getPathParts();
}
#Override
protected List<FooBarPathPart> getPathParts(String path) {
return Arrays.stream(path.split("/")).map(FooBarPathPart::new).collect(Collectors.toList());
}
}
where I'd like to capture the structure of a filesystem path /my/path/to/a/directory in my Path object, which stores my, path, to, a, directory each as a PathPart object.
Now, I have a subclass of PathPart called FooBarPathPart, where if the path part is equal to foo, then I want it to change it to bar. And, I also have FooBarPath which is a subclass of Path, which stores a list of FooBarPathPart. So /my/path/to/foo/directory will essentially become /my/path/to/bar/directory
My issue is that I get an Unchecked cast warning from List<? extends PathPart> to List<FooBarPath> in my getPathParts() method in the FooBarPath class.
Is there a way to get rid of this unchecked cast warning properly? Is my usage of the wildcard correct here? Or is there a better way to approach this problem that doesn't involve the wildcard? I'm not very familiar with generics
Thanks for the help Thomas
I have solved the problem using generics and using a creator function. Here's the full solution:
public class PathPart {
private final String pathPart;
public PathPart(String pathPart) {
this.pathPart = pathPart;
}
public String getPathPart() {
return pathPart;
}
}
public class FooBarPathPart extends PathPart {
public FooBarPathPart(String pathPart) {
super(isFoo(pathPart) ? "bar" : pathPart);
}
private static boolean isFoo(String pathPart) {
return "foo".equals(pathPart);
}
}
public abstract class AbstractPath<T extends PathPart> {
private final List<T> pathParts;
private final Function<String, T> factory;
public AbstractPath(Function<String, T> factory, String path) {
this.factory = factory;
this.pathParts = createPathParts(path);
}
public List<T> createPathParts() {
return this.pathParts;
}
private List<T> createPathParts(String path) {
return Arrays.stream(path.split("/")).map(factory).collect(Collectors.toList());
}
}
public class Path extends AbstractPath<PathPart> {
public Path(String path) {
super(PathPart::new, path);
}
}
public class FooBarPath extends AbstractPath<FooBarPathPart> {
public FooBarPath(String path) {
super(FooBarPathPart::new, path);
}
}
I implemented the following Java classes:
public class Data<T> {
private List<T> data;
public List<T> getData() {
return this.data;
}
public Data<T> setData(List<T> data) {
this.data = data;
return this;
}
public Data<T> getAsNullIfEmpty() {
if (this.data == null || this.data.isEmpty())
return null;
return this;
}
}
public class IntegerData extends Data<Integer> {}
I would like the getAsNullIfEmpty() method to be able to be called by its subclasses instances.
The line IntegerData integerData = new IntegerData().getAsNullIfEmpty(); throws the following compilation error:
incompatible types: Data<java.lang.Integer> cannot be converted to IntegerData
I tried changing the body of the method getAsNullIfEmpty() to this:
public <E extends Data<T>> E getAsNullIfEmpty() {
if (this.data == null || this.data.isEmpty())
return null;
return this;
}
This doesn't compile though because Data<T> does not extend itself.
Is there a way to accomplish this without recurring to overriding the method in each of the child classes or using an explicit cast?
You need the "usual" trick for having a class's generics refer to itself, plus an unsafe cast.
public class Data<T, D extends Data<T, D>> {
private List<T> data;
public List<T> getData() {
return this.data;
}
public D setData(List<T> data) {
this.data = data;
return (D) this;
}
public D getAsNullIfEmpty() {
if (this.data == null || this.data.isEmpty())
return null;
return (D) this;
}
}
public class IntegerData extends Data<Integer, IntegerData> { ... }
I have some difficulty to simplify more the problem. Sorry if they are too many code here.
I try to improve the architecture of the code above because I hate warning and cast and I feel something wrong.
Now, the code.
I have a util class with these two parametrized methods (same signature as OpenJPA's CriteriaBuilder...)
public class MyUtil {
public void equal(List<?> l, Object value) {
// do something (see CriteriaBuilder.equal method)
}
public <Y extends Comparable<? super Y>> void greaterThan(List<? extends Y> l, Y value) {
// do something (see CriteriaBuilder.greaterThan method)
}
}
Then, I want to be able to abstract them to call it via an interface.
public interface IOperation<T> {
// maybe make this method generic ? but how ?
public abstract void doOp(List<T> l, T value);
}
public abstract class AbstractOperation<T> implements IOperation<T> {
protected MyUtil myUtil;
}
public class EqualOp extends AbstractOperation<Object> {
#Override
public void doOp(List<Object> path, Object value) {
myUtil.equal(path, value);
}
}
public class GreaterThanOp<T extends Comparable<? super T>> extends AbstractOperation<T> {
#Override
public void doOp(List<T> path, T value) {
myUtil.greaterThan(path, value);
}
}
I create a factory
public class OperationFactory {
private static OperationFactory instance;
public static OperationFactory getInstance() {...}
public IOperation<?> get(String op) {
if ("=".equals(op)) {
return new EqualOp();
} else if (">".equals(op)) {
return new GreaterThanOp<Comparable<? super Object>>();
}
throw new InvalidParameterException();
}
}
Then I use it :
public class Client {
public void needOp(String op) {
IOperation<String> operation = (IOperation<String>) OperationFactory.getInstance().get(op); // How to avoid this cast ?
List<String> l = null;
operation.doOp(l, "a string");
}
}
My question is : is it possible to avoid this cast in the Client class ? How ? Is there a way to have a better architecture ?
Thanks for reading
I'm assuming you can require your type to be Comparable.
Parameterize EqualOp like GreaterThanOp:
public class EqualOp<T extends Comparable<T>> extends AbstractOperation<T> {
#Override public void doOp(List<T> path, T value) ...
And define get() like this:
public <T extends Comparable<T>> IOperation<T> get(String op) {
if ("=".equals(op)) {
return new EqualOp<T>();
} else if (">".equals(op)) {
return new GreaterThanOp<T>();
}
...
I'm trying to create public class MyClass<T extends Parcelable> implements Parcelable. I'm having trouble implementing Parcelable. Is it possible to create a generic class that implements Parcelable? (Note that T is bounded so that it also must implement Parcelable).
I am running into trouble with the fact that the Parcelable interface requires a static variable: public static final Parcelable.Creator<MyParcelable> CREATOR. Thus I cannot do public static final Parcelable.Creator<MyClass<T>> CREATOR because MyParcelable<T> is nonstatic.
André
I had similar issues with implementing Parcelable on a class with a generic, the first issue was the same as what you were experiencing:
Thus I cannot do public static final Parcelable.Creator> CREATOR because MyParcelable is nonstatic.
The second was to read in a Parcelable object you need access to the ClassLoader which cannot be gotten from T due to type erasure.
The class below is an adaption of a class I am using in production which overcomes both issues. Note: I have not tested this class specifically, so let me know if you have any issues.
public class TestModel<T extends Parcelable> implements Parcelable {
private List<T> items;
private String someField;
public List<T> items() {
return items;
}
public void setItems(List<T> newValue) {
items = newValue;
}
public String someField() {
return someField;
}
public void setSomeField(String newValue) {
someField = newValue;
}
//region: Parcelable implementation
public TestModel(Parcel in) {
someField = in.readString();
int size = in.readInt();
if (size == 0) {
items = null;
}
else {
Class<?> type = (Class<?>) in.readSerializable();
items = new ArrayList<>(size);
in.readList(items, type.getClassLoader());
}
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(someField);
if (items == null || items.size() == 0)
dest.writeInt(0);
else {
dest.writeInt(items.size());
final Class<?> objectsType = items.get(0).getClass();
dest.writeSerializable(objectsType);
dest.writeList(items);
}
}
public static final Parcelable.Creator<TestModel> CREATOR = new Parcelable.Creator<TestModel>() {
public TestModel createFromParcel(Parcel in) {
return new TestModel(in);
}
public TestModel[] newArray(int size) {
return new TestModel[size];
}
};
//endregion
}
Write the generic data member class name to the parcel and then read it back in order to create its class loader. Example,
public class MyClass<T> implements Parcelable {
T data;
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(data.getClass().getName());
dest.writeParcelable((Parcelable) data, 0);
}
private MyClass(Parcel in) {
final String className = in.readString();
try {
data = in.readParcelable(Class.forName(className).getClassLoader());
} catch (ClassNotFoundException e) {
Log.e("readParcelable", className, e);
}
}
Yes you can. You just need to store the class name or class loader during the construction of your subclass object and then you can pass it during the read/write operation of the parcelable.
Step by step instructions:
Step 1. Store the class name that extends from your Generic class like this:
public abstract class GenericClass<T> implements Parcelable {
private String className;
Step 2. Any classes that extends from your generic class must specify the class name during its construction like this:
public class MyClass extends GenericClass<MyClass> {
public MyClass () {
super();
setClassName(MyClass.class.getName()); // Generic class setter method
}
Step 3. In your generic class, you can then read/write your class names to getClassLoader() like this:
public abstract class GenericClass<T> implements Parcelable {
private String className;
T myGenericObject;
protected MyClass (Parcel in) {
super(in);
this.className = in.readString();
ClassLoader classLoader;
try {
classLoader = Class.forName(this.className).getClassLoader();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
myGenericObject = in.readParcelable(classLoader);
//... Other class members can go here
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(className);
//... Other class members can go here
}
}
Based on answers above, have created extension functions for this.
fun <T : Parcelable> Parcel.writeGenericParcelable(data: T, flags: Int) {
writeString(data::class.java.name)
writeParcelable(data, flags)
}
fun <T : Parcelable> Parcel.readGenericParcelable(): T {
val className = readString()!!
val classNameLoader = Class.forName(className).classLoader
return readParcelable(classNameLoader)!!
}
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