开发者

Warnings in Java when casting to a generic type

I have some generic code which I cannot figure out how to legitimately prevent getting warnings from; I am using @SuppressWarnings("unchecked") for the moment, since it seems that casting a generic type can't be done without warnings.

How can I get rid of the annotation?

What I have is:

public MyObject(SharedContext<Object> ctx) {
    super(ctx); // set protected field 'context'
    ...
    context.set("Inpu开发者_如何学Pythont Fields"  ,Collections.synchronizedMap(new TreeMap<String,Pair<String,Boolean>>(String.CASE_INSENSITIVE_ORDER)));
    context.set("Output Fields" ,Collections.synchronizedMap(new TreeMap<String,String>              (String.CASE_INSENSITIVE_ORDER)));
    context.set("Event Registry",new EventRegistry(log)                                                                              );
    }

@SuppressWarnings("unchecked")
protected void startup() {
    inputFields     =(Map<String,Pair<String,Boolean>>)context.get("Input Fields"  ,null);
    outputFields    =(Map<String,String>              )context.get("Output Fields" ,null);
    eventRegistry   =(EventRegistry                   )context.get("Event Registry",null);
    ...
    }

The protected variable context is type SharedContext<Object>.

Without the annotation the compiler gives warnings:

...\MyClass.java:94: warning: [unchecked] unchecked cast
found   : java.lang.Object
required: java.util.Map<java.lang.String,com.mycompany.Pair<java.lang.String,java.lang.Boolean>>
    inputFields     =(Map<String,Pair<String,Boolean>>)context.get("Input Fields"  ,null);
                                                                  ^
...\MyClass.java:95: warning: [unchecked] unchecked cast
found   : java.lang.Object
required: java.util.Map<java.lang.String,java.lang.String>
    outputFields    =(Map<String,String>              )context.get("Output Fields" ,null);


After some further research I believe I have found a reasonable alternative, which at least limits the suppression annotation to just one global static utility method to do an unchecked cast.

The self contained test program which follows should be clear enough:

public class Generics
{

static public void main(String[] args) {
    Generics.test();
    }

static private void test() {
    Map<String,Object> ctx=new TreeMap<String,Object>();
    Map<String,Object> map=new TreeMap<String,Object>();
    Map<String,Object> tst;

    ctx.put("Test",map);
    tst=uncheckedCast(ctx.get("Test"));
    }

@SuppressWarnings({"unchecked"})
static public <T> T uncheckedCast(Object obj) {
    return (T)obj;
    }

}

Another blog suggested an improvement to this utility method:

@SuppressWarnings("unchecked") 
public static <T, X extends T> X uncheckedCast(T o) {
    return (X) o;
    }

forcing what is returned to be a subclass of the parameter passed in.

Assuming I put uncheckedCast into public utility class GenUtil, my startup method in the question would have no (useless) warnings emitted and look like:

protected void startup() {
    inputFields  =GenUtil.uncheckedCast(context.get("Input Fields"  ,null));
    outputFields =GenUtil.uncheckedCast(context.get("Output Fields" ,null));
    eventRegistry=GenUtil.uncheckedCast(context.get("Event Registry",null));
    ...
    }


The first unchecked cast can be eliminated by defining a non-generic class that extends the generic Map<String, Pair<String, Boolean>> and storing that in the SharedContext instead of a generic TreeMap, e.g. (using ForwardingMap from Guava):

class InputFieldMap extends ForwardingMap<String,Pair<String,Boolean>> {

    private final Map<String,Pair<String,Boolean>> delegate =
        Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
    protected Map<String,Pair<String,Boolean>> delegate() { return delegate; }

}

// ...

context.set("Input Fields"  ,Collections.synchronizedMap(new InputFieldMap()));

// ...

inputFields     =(InputFieldMap)context.get("Input Fields"  ,null);
outputFields    =(Map<?,?>     )context.get("Output Fields" ,null);

You could make the second cast safe in the same way, or (assuming you are only reading the map not modifying it) use the map as is (with wildcard parameters) and convert the value to a string with each lookup:

String bar = String.valueOf(outputFields.get("foo"));

or wrap the map:

Map<?, String> wrappedOutputFields    =
    Maps.transformValues(outputFields, Functions.toStringFunction());

// ...

String bar = wrappedOutputFields.get("foo");


Is the SharedContext object one that you wrote? If so, is it possible to replace the generic String->Object mapping with specific fields?

eg.

context.setInputFields(...)
context.setOutputFields(...)
context.setEventRegistry(...)
context.getInputFields()
etc.

The generic hold-all context object always seems a less-than-perfect solution to me. Especially with generics and the unchecked cast messages that results.

Alternatively, you could create a wrapper object called SoftwareMonkeyContext that has the specific setter/getter methods as above, and internally uses your GenUtil.uncheckedCast method. This would prevent you needing to use GenUtil.uncheckedCast at multiple spots in your code.


How is the context variable being assigned? Is it from the parameter ctx, which is of type :

SharedContext<Object>

?

If so, thats your problem because when you do a get you haven't effectively typed what you are getting.


Which version of compiler you are using? With Java 6 compiler(Sun JDK windows) I did not see detailed warning. I am getting warning information only when I use '-Xlint:unchecked' flag.

Try -Xlint:-unchecked and let us know if it resolves your issue. For more on flags,

http://java.sun.com/javase/6/docs/technotes/tools/windows/javac.html

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜