I'm trying to create DI container get method, but struggling with signature. Currently I have this definition:
public Object get(Class<?> key) {
// returns instance of `?`
}
The part of my code which I dont like much is usage of the get method:
IRouter router = (IRouter) container.get(IRouter.class);
where I have to cast return with (IRouter). Any ideas how to change method signature to make usage like this?
IRouter router = container.get(IRouter.class);
Thanks in advance for any ideas!
By using a scoped method parameterized type :
public <T> T get(Class<T> key) {
// ...
return (T) foo;
}
Here I suppose that foo is not typed as T.
If it is already typed as T you can of course return it without cast.
You could so invoke it :
IRouter router = container.get(IRouter.class);
Related
This question is more theoretical (what I want to do is more complicated but this is the part I'm stuck on), so apologies for the contrived example which may not make much sense.
Say I have some class that has methods that return its value in different forms:
public class MyObject {
public String getAsString() {...}
public int getAsInt() {...}
// and so on
}
I'm trying to create a single method to allow me to specify which MyObject method to call via its parameters. Something like:
public <T> T getValue(MyObject obj, Class<T> c) {
if (c == String.class) {
return obj.getAsString();
} else if (c == Integer.class) {
return obj.getAsInt();
} // and so on
}
So then I would like to call this method like this, assuming obj is a MyObject:
String s = getValue(obj, String.class);
int i = getValue(obj, Integer.class);
// and so on
I'm getting the compile error "Type mismatch: cannot convert from String to T" (and likewise for Integer) in the getValue method. Clearly I'm just not understanding generics fully, but I thought this was the general idea behind generics - here I'm specifying (or trying to specify, at least) the real type of T via the parameter c. What am I doing wrong?
If you want to to create a single method with really safe casts - then I would suggest to setup a mapping between the expected type and its respective getter.
Given the MyObject class definition as:
public class MyObject {
public int getIntValue() {
return 42;
}
public String getStringValue() {
return "Answer";
}
}
So that the "accessor" class could look as follows (it can be generalized further if needed):
public class MyObjectAccessor {
private final Map<Class<?>, Function<MyObject, ?>> registry = new HashMap<>();
public Accessor() {
registerGetter(Integer.class, MyObject::getIntValue);
registerGetter(String.class, MyObject::getStringValue);
}
private <T> void registerGetter(Class<T> type, Function<MyObject, T> getter) {
registry.put(type, getter);
}
#SuppressWarnings("unchecked")
public <T> Optional<T> getValue(MyObject obj, Class<T> type) {
return (Optional<T>) ofNullable(registry.get(type)).map(getter -> getter.apply(obj));
}
}
This would allow you to make the behavior much more predictable with a control over the unexpected/missing mapping.
(Here it returns an Optional back, but you can also throw an exception or provide a default value or do something else)
Please note that the cast inside getValue is actually a safe checked cast (even though it was marked with #SuppressWarnings) as the "safety" proof here is a little bit beyond current javac's capability of static code analysys.
First of all, if getAsString and getAsInt are not doing any conversion (such as would be the case if all your values were stored as strings), you probably can reduce your method to this:
public <T> T getValue(MyObject obj) {
return (T) obj.value;
}
This will have an unchecked cast warning, but that's not worse than leaving the typing decision to your caller (so I'd just #SuppressWarnings("unchecked") it). If your caller uses the wrong target type, they will get a ClassCastException at runtime, which I assume goes well with your current contract. But you can keep c.cast(obj.getAsX()) if you want the exception to be raised in your own method.
With the above, your callers would just use:
String s = getValue(obj);
int i = getValue(obj);
If, however, you are actually converting data in getAs... methods, then you will need to cast in your generic getter after dispatching to the correct getAsX method, at least as ProGu suggested (i.e., return c.cast(obj.getAsX()) in each branch).
I am working with Java Generic classes (in this example, these are the Collection classes), and Reflection. I would like to be able to use reflection to take in a Class, check if it is an instance of a List, and then invoke the add method to it.
However, I've faced some difficulties in trying to put as the parameters to invoke the method call, and getting the declared method (shown where I put-what???). Both of those method parameter calls, require an object of type Class<?> which is the parameter type of needed for the add methods being invoked, which I don't know, since T itself is a generic.
Any help is appreciated! I apologize if the question is unclear, I tried the best I could to clarify.
static <T> void TestACollection(Class<T> clazz) {
T element=clazz.newInstance();
if(element instanceof List<?>)
Method m=clazz.getDeclaredMethod("add", what??? );
m.invoke(element, what???);
}
I'm guessing what you are trying to do is this:
public static <T> List<T> makeList() {
List<T> list = (List<T>) new ArrayList();
return list;
}
//...
{
List<String> list = makeList();
list.add( "Howdy" );
}
Which works as-is in Java 8. In earlier versions you may have to add #SuppressWarnings("unchecked") to the assignment.
I have the following interface:
public interface IRadioButtonGroup<T> {
List<IRadioButton<T>> getButtons();
}
Now I create a method
protected Object getDefaultRadioButtonValue(IRadioButtonGroup<?> field) {
List<IRadioButton<?>> buttons = field.getButtons();
Now java is complaining:
Type mismatch: cannot convert from List<IRadioButton<capture#1-of ?>> to List<IRadioButton<?>>
Any suggestions?
The unbounded wildcard parameter type of your method means that, it will accept an IRadioButtonGroup of an unknown type. The compiler doesn't know what type will come, but at compilation time, the compiler does generate and assign each wildcards with a placeholder, because although it doesn't know which type is coming, it is sure that there has to be a single type that will replace ?. And that placeholder is capture#1-of ? you see in the error message.
Basically, you are trying to assign a List<IRadioButton<Cap#1-of-?>> to a List<IRadioButton<?>>, and that is not valid, in a similar way how List<List<String>> cannot be assigned to List<List<?>>.
One well known way to solve these issues is to use capture helpers. Basically you create a generic method, and delegate the call to that method. That generic method will infer the type parameter and then you would be able to do type safe operation. See this Brian Goetz's article for more details.
So to solve the issue, provide another method as in below code:
protected Object getDefaultRadioButtonValue(IRadioButtonGroup<?> field) {
return getDefaultRadioButtonValueHelper(field);
}
private <T> Object getDefaultRadioButtonValueHelper(IRadioButtonGroup<T> field) {
List<IRadioButton<T>> buttons = field.getButtons();
// Write the logic of original method here
}
List<IRadioButton<?>> buttons = new List<IRadioButton<?>>();
foreach(IRadioButton button in field.getButtons())
{
buttons.Add(button);
}
You could do:
protected <T> Object getDefaultRadioButtonValue(IRadioButtonGroup<T> field) {
List<IRadioButton<T>> buttons = field.getButtons();
return buttons;
}
or
protected Object getDefaultRadioButtonValue(IRadioButtonGroup<?> field) {
return field.getButtons();
}
Change:
protected Object getDefaultRadioButtonValue(IRadioButtonGroup<?> field) { List<IRadioButton<?>> buttons = field.getButtons();
to:
protected Object getDefaultRadioButtonValue(IRadioButtonGroup<IRadioButton<?>> field) {
List> buttons = field.getButtons();
I'm having trouble figuring out how to properly cast a generic object in java to a type that extends the generic object.
For example, say I some setup like the following:
public class Parameters extends SomeCustomMap<String, String>
{
...
}
public class SomeCustomMap<K, V> implements Map<K, V>
{
public SomeCustomMap<K, V> getSubSet(...)
{
SomeCustomMap<K, V> subset;
...
return subset;
}
}
class ExampleApp
{
private void someMethod()
{
Parameters params;
Parameters paramsSubSet;
try
{
...
paramsSubSet = (Parameters) params.getSubSet(...);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Running code similar to the above consistently throws a ClassCastException, the likes of which I do not fully understand. Any assitence for how to correctly set up a scenario similar to the above would be appreciated! Namely, how might I properly cast the the SomeCustomMap object that is returned from the params.getSubSet(...) method back to a Parameters object?
Thanks in advance!
Your Problem is that the Subset returned by getSubSet is a of instance SomeCustomMap and not of Parameters.
This problem does not deal with generics. You will get the same problem if you did not use generics.
I don't know how you create an instance of subset but maybe you could use the template desing pattern and some generics to fix your problem.
You can try something like this:
public <T extends SomeCustomMap<K, V>> T getSubSet(...){
T subset = (T)this.clone();
subset.clear();
return subset;
}
creation looks a little funny - feel free to change it to whatever you want :)
As a bonus you will not need to cast :)
paramsSubSet = params.getSubSet(...)
Though I've commented asking for more information, based on what you've posted so far, I think getSubSet is constructing a SomeCustomMap to return (with new SomeCustomMap) somewhere. If you don't override getSubSet in Parameters, then Parameters.getSubset will return a SomeCustomMap (the base class), not a Parameters, so your typecast to Parameters fails.
(Hot tip, if you override getSubSet in the Parameters class, you can change the return type to Parameters and avoid the typecast.)
Generics don't inherently have anything to do with casting (save that due to the nature of erasure, generic parameters cannot be checked during a cast).
If you're getting a ClassCastException in this case, it means that the object returned really is not an instance of Parameters. Just before you cast, try calling
System.out.println(params.getSubSet(...).getClass());
and see what the actual run-time class of the subset is. Chances are the problem lies elsewhere, as your expectation that the subset is a Parameters object is almost certainly not correct at runtime - it's a SomeCustomMap or some other subclass thereof.
As others have explained, the issue is that the actual object you are constructing in getSubSet() is not an instance of Parameters.
Here's one possible workaround. I don't love it, but it is a way to declare the method in SomeCustomMap but have its return value be typed correctly for any subclass.
public static <T extends SomeCustomMap<K, V>> getSubSet(T fullSet)
{
T subset;
... (use fullSet instead of this)
return subset;
}
In my project I have a factory method that loads an object that implements an interface. You pass in the class you desire and receive an instantiation of it, like so.
public class Factory {
public static <E extends SomeInterface> E load( Class<E> clss ) throws Exception {
return clss.newInstance();
}
}
You could invoke it like this:
MyObject obj = Factory.load( MyObject.class );
This code works just fine in Eclipse 3.4 with Java 6u13, however today I received a new laptop and installed Eclipse 3.5 and java 6u15 and now I am getting type mismatches everywhere.
MyObject obj = Factory.load( MyObject.class );
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Type mismatch: cannot convert from SomeInterface to MyObject
Putting a cast before Factory on that line makes it go away and all runs well, but it makes the line a bit less clean, and I didn't need it before, so what gives?
Did you recently add a type parameter to your factory class? There's a pitfall with generic methods on raw types:
public class FooFactory<UnrelatedArg> {
public <E> E load(Class<E> c) { ... }
}
FooFactory<?> f; f.load(String.class); // returns String
FooFactory f; f.load(String.class); // returns Object
Is that all the code required to get this bug? I've seen something very similar in some code I've been looking at today. There was an additional parameter being passed into the equivalent of your Factory method which had a generic type as well. This was missing it's generic definition and I think was to blame for confusing the compiler.
ie, if your factory method looked something like
public class Factory {
public static <E extends SomeInterface> E load( Class<E> class, Key key ) {
// return an instance of E
}
}
Where there is some Key class defined something like this
public class Key<Datatype> {
....
}
Giving something like this to invoke the method, note no generics on the declaration of key
Key key = new Key()
MyObject obj = Factory.load( MyObject.class, key );
Hope that helps,
I think this is related to the Java compilance level. By default a project has the default level. Which you set in the Eclipse preferences. In your old installation you will have change it.
https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643