Hope somebody can help me out of this confussion.
I made this method:
public static <T> void myMethod(Map<Class<T>, MyInterface<T>> map) {
}
Used paramter T in order to make sure that the class used as key is the same as the class used as parameter in MyInterface.
Now I want to pass a map which different classes as keys, of course, and corresponding implementations of MyInterface.
But it doesn't work, getting syntax errors because of type parameters. Here is the code, I hope is self explanatory.
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
Map<Class<?>, MyInterface<?>> map = new HashMap<Class<?>, MyInterface<?>>();
// Map<Class<Object>, MyInterface<Object>> map = new HashMap<Class<Object>, MyInterface<Object>>();
map.put(Object.class, new MyObjectImpl());
//if I use Map<Class<Object>, MyInterface<Object>> I get a compiler error here
//because map<String> is not map<Object> basically
map.put(String.class, new MyStringImpl());
//this would be possible using <?>, which is exactly what I don't want
// map.put(String.class, new MyIntegerImpl());
//<?> generates anyways a compiler error
myMethod(map);
}
//use T to make sure the class used as key is the same as the class of the parameter "object" in doSomething
public static <T> void myMethod(Map<Class<T>, MyInterface<T>> map) {
}
interface MyInterface<T> {
void doSomething(T object);
}
static class MyObjectImpl implements MyInterface<Object> {
#Override
public void doSomething(Object object) {
System.out.println("MyObjectImpl doSomething");
}
}
static class MyStringImpl implements MyInterface<String> {
#Override
public void doSomething(String object) {
System.out.println("MyStringImpl doSomething");
}
}
static class MyIntegerImpl implements MyInterface<Integer> {
#Override
public void doSomething(Integer object) {
System.out.println("MyIntegerImpl doSomething");
}
}
}
You can't do that, because there is no constraint defined in Map's put() method between the key and the value. If you want to assure that your map is populated properly (i.e. create such constraint), hide the map behind some API that will check the correctness, for example:
public <T> void registerInterface(Class<T> clazz, MyInterface<T> intf) {
map.put(clazz, intf);
}
Then, just call the registerInterface instead of manually populating the map.
As far as I know, you cannot declare a Map like you describe in Java. All you can do is performing type checking and/or add constraints.
Guava offers something that approaches your problem with ClassToInstanceMap. So one way to do this would be to use MapConstraints.constrainedMap (like the example below)
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.MapConstraint;
import com.google.common.collect.MapConstraints;
public class Main {
interface MyInterface<T> {
void doSomething(T object);
Class<T> getType();
}
static class MyObjectImpl implements MyInterface<Object> {
#Override
public void doSomething(Object object) {
System.out.println("MyObjectImpl doSomething");
}
#Override
public Class<Object> getType() {
return Object.class;
}
}
static class MyStringImpl implements MyInterface<String> {
#Override
public void doSomething(String object) {
System.out.println("MyStringImpl doSomething");
}
#Override
public Class<String> getType() {
return String.class;
}
}
static class MyIntegerImpl implements MyInterface<Integer> {
#Override
public void doSomething(Integer object) {
System.out.println("MyIntegerImpl doSomething");
}
#Override
public Class<Integer> getType() {
return Integer.class;
}
}
public static void main(String[] args) throws ParseException {
Map<Class<?>, MyInterface<?>> map = MapConstraints.constrainedMap(new HashMap<Class<?>, Main.MyInterface<?>>(),
new MapConstraint<Class<?>, MyInterface<?>>() {
#Override
public void checkKeyValue(Class<?> key, MyInterface<?> value) {
if (value == null) {
throw new NullPointerException("value cannot be null");
}
if (value.getType() != key) {
throw new IllegalArgumentException("Value is not of the correct type");
}
}
});
map.put(Integer.class, new MyIntegerImpl());
map.put(String.class, new MyStringImpl());
map.put(Object.class, new MyObjectImpl());
map.put(Float.class, new MyIntegerImpl()); //<-- Here you will get an exception
}
}
I do not think this is possible :
Class<T> only ever accepts T.class as value. Class<Object> will not accept String.class, even though Object is a superclass of String.
For this reason any map with Class<T> as key can have only one element, with T.class as key value, whatever the value of T.
The compiler will only ever accept a map with a definite value of T as parameter. You cannot write Map<Class<?>, MyInterface<?>> because each ? is assumed to be different : it does not match Map<Class<T>, MyInterface<T>> which requires T to have the same value.
That said, myMethod will only ever accept single-entry maps, which does not seem useful.
Change your method signature to
public static <T> void myMethod(Map<Class<? extends T>, MyInterface<? extends T>> map) {
}
now your declaration and invocation should work..
Map<Class<?>, MyInterface<?>> map = new HashMap<Class<?>, MyInterface<?>>();
map.put(Integer.class, new MyIntegerImpl());
map.put(String.class, new MyStringImpl());
map.put(Object.class, new MyObjectImpl());
myMethod(map);
Related
I have a couple of classes having identical methods, except with respect to certain parameter types:
interface ICls<T> {
void doSomething(String key, T value);
Map<String, T> getSomething();
}
class ClsA implements ICls<Boolean> {
#Override public void doSomething(String key, Boolean value) { }
#Override public Map<String, Boolean> getSomething() { return Map.of(); }
}
class ClsB implements ICls<String> {
#Override public void doSomething(String key, String value) {}
#Override public Map<String, String> getSomething() { return Map.of(); }
}
Now I'm trying to have a main class that stores a mixed list of these class objects and for each of these instances, passes info between its two methods:
class Main {
List<ICls<?>> list = List.of(
new ClsA(),
new ClsB()
);
void run() {
list.forEach(cls -> {
Map<String, ?> data = cls.getSomething();
data.keySet().forEach(key -> cls.doSomething(key, data.get(key)));
});
}
The List<ICls<?>> and Map<String, ?> statements are OK. However, the map.get(key) throws an IDE error:
'doSomething(<String, capture<?>>) in '...ICls' cannot be applied to 'String, capture<?>'
Hovering the mouse cursor over the offending statement shows:
Required type: capture of ?
Provided: capture of ?
Assuming that I can't/don't want to change the generic type T to Object, and don't want to change the architecture either, what can I do to make the code here compile?
I've tried changing the signature of doSomething so that it accepts the entire Map<String, T> and call it like so, with no luck either:
cls.doSomething(cls.getSomething());
This compiles for me:
import java.util.List;
import java.util.Map;
public class Comparison {
interface ICls<T> {
void doSomething(String key, T value);
Map<String, T> getSomething();
}
static class ClsA implements ICls<Boolean> {
public void doSomething(String key, Boolean value) {}
public Map<String, Boolean> getSomething() { return null; }
}
static class ClsB implements ICls<String> {
public void doSomething(String key, String value) {}
public Map<String, String> getSomething() { return null; }
}
static class Main {
List<ICls<?>> list = List.of(
new ClsA(),
new ClsB()
);
void run() {
list.forEach(cls -> {
doIt(cls);
});
}
<T> void doIt(ICls<T> cls) {
Map<String, T> data = cls.getSomething();
data.keySet().forEach(key -> cls.doSomething(key, data.get(key)));
}
}
}
It makes clear the relationship between the map and the cls.
In the original context, because the type of the List is ICls<?> we can't get that relationship, but once we get a single ICls we can introduce a type variable T which allows us to express the relationship between getSomething and doSomething.
I want to implement a class that instantiates generic types.
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public void doSomething(E Element) {
T set = setClass.newInstance();
set.add(element);
}
}
I tried instantiating the class like this:
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet<>(HashSet<Integer>.class);
However using .class on a generic type does not seem to be allowed. How would I correctly pass the required Class of a generic type to the constructor?
Not sure it is good to expose the inner set type (Hash versus other) in the parameterized type.
Actually due to type erasure you can't instantiate parameterised types directly, but you can pass in a factory,
package langGenerics;
import java.util.HashSet;
import java.util.Set;
public class UseGenerics {
public static void main(String[] args) {
SetFactory<Integer> setFactory = HashSet::new;
DisjointSet<Integer> disjointSet = new DisjointSet<>(setFactory);
disjointSet.doSomething( 123 );
}
}
interface SetFactory<T> { Set<T> get(); }
class DisjointSet<T> {
private SetFactory<T> setFactory;
public DisjointSet(SetFactory<T> setFactory) {
this.setFactory = setFactory;
}
public void doSomething(T item) {
Set<T> set = setFactory.get();
set.add(item);
}
}
If you really want to init your own set storage, then I suggest you to pass Supplier to your constructor:
public static class DisjointSet<T extends Set<E>, E> {
T set;
public DisjointSet(Supplier<T> supplier) {
set = supplier.get();
}
public void doSomething(E element) {
set.add(element);
}
}
Then use it:
DisjointSet<HashSet<Integer>, Integer> set = new DisjointSet<>(HashSet::new);
if this is what you wanted,
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public static void main(String[] args) {
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet(new HashSet<Integer>().getClass());
}
}
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 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
I had an interface initially as below.
public interface testMe {
public Set<String> doSomething();
}
public class A implements testMe {
public Set<String> doSomething() {
return // Set<String>
}
}
I had similar classes implementing testMe. Now I have to add one more class which returns Set<Some Object>
public class X implements testMe() {
public Set<Some OBject> doSomething() {
}
}
How could i add this method in the interface without breaking existing classes?
You can use
public interface testMe {
public Set<?> doSomething();
}
Or
public interface testMe {
public Set<? extends CommonSuperclass> doSomething();
}
You can't for two reasons.
A class or interface can't have two or more methods that have the same number and type of parameters with the same name but differing return types; and
Because of type erasure, all Set<...> instances are, at runtime, simply Set, so they would have the exact same return type anyway.
You will need to name the second something different.
The more complicated answer is that you can make the parameter type extensible:
public interface TestMe<T extends Serializable> {
Set<T> doSomething();
}
public class A implements TestMe<String> {
#Override
public Set<String> doSomething() { ... }
}
public class X implements TestMe<ASerializableObject> {
#Override
public Set<ASerializableObject> doSomething() { ... }
}
I don't believe you can, because type erasure will ruin the effect you have in mind.
You can parameterize the interface:
import java.util.Set;
public interface ISomething<T>
{
Set<T> doSomething(T [] data);
}
And the implementation:
import java.util.HashSet;
import java.util.Set;
public class Something<T> implements ISomething<T>
{
public static void main(String[] args)
{
Something<String> something = new Something<String>();
Set<String> set = something.doSomething(args);
System.out.println(set);
}
public Set<T> doSomething(T [] data)
{
Set<T> foo = new HashSet<T>();
for (T x : data)
{
foo.add(x);
}
return foo;
}
}
I'm not sure this accomplishes what you have in mind, though.