简体   繁体   中英

How to avoid unsafe cast warnings with Java Generics

I'm quite new to Java Generics. But I've read a lot of summary links and examples. But I cannot get the simplest method right. I hope somenoe can help:

I want to create a HashMap which maps one undefined Object to another one. Both objects may one of String or Integer.

So I wrote this:

private final HashMap<L, R> left2Right = new HashMap<L, R>();

Extractor<?> extLeft  = Extractor.getInstance(0);
Extractor<?> extRight = Extractor.getInstance(1);

L leftVal = extLeft.extract(d, i);
R rightVal = extRight.extract(d, i);

this.left2Right.put(leftVal, rightVal);

So far so good... But I have problems implementing the extractor-objects. They are instanciated by a factory pattern. I did it like this (simplyfied):

abstract class Extractor<E> {
    abstract E extract(DTable source, int row);

    static <E> Extractor<E> getInstance(int type) {
        if(type == 0)
            return new IntExtractor();
        else
            return new StringExtractor();
    }
}

class IntExtractor extends Extractor<Integer> {

    @Override
    Integer extract(DTable source, int row) {
        int value = 5;
        return new Integer(value);
    }

}

class StringExtractor extends Extractor<String> {

    @Override
    String extract(DTable source, int row) {
        String retVal = "hello";
        return retVal;
    }

}

It compiles, but I get Unsave cast warnings on casting the Integer/String to E. What am I doing wrong? I know, I can simply supress the warnings. But I thought exactly this should be the advantage of Java generics? I cannot make this cast save, because I dont know, which type 'E' really "is"...

Or am I doing something basically wrong?

Note: I edited my code to a "new" question after I used some information from the first answer...

Your extractors aren't really generic at all. It sounds like you want something like:

public interface Extractor<P> {
    P extract(DTable source, int row);
}

but then make the implementations non-generic:

class IntExtractor implements Extractor<Integer> {

    Integer extract(DTable source, int row) {
        int value = 5;
        return new Integer(value);
    }
}

Fundamentally your current code is broken, in that I could write:

IntExtractor<String> extractor = new IntExtractor<String>();
String x = extractor.extract(...);

... that would obviously fail, but only at execution time.

I'd say you're doing something basically wrong. If it is an IntExtractor and a StringExtractor, you don't need generics. Just define methods

Integer extract(DTable source, int row)

and

String extract(DTable source, int row)

The Problem isn't your code per-se. The task itself cannot be solved without unsafe casts. Read your own sentence again:

I want to create a HashMap which maps one undefined Object to another one. Both objects may one of String or Integer.

Java Generics cannot solve this problem for you, because you have to know the type beforehand, otherwise you have to cast.

To be honest, I do not know what you want to achieve with the code you posted, but If you want to use a HashMap, you can do it like this:

private final HashMap<String, Integer> left2Right = new HashMap<String, Integer>();
left2Right.put("one", 1);
int numberOne = left2Right.get("one");

In this case you do not have to cast the values, because the HashMap has String keys and Integer values. If you want to put different types as values, you have to use a supertype of all values. This super type might be Object, the root of the hierarchy. But in those cases you have to cast - because you do not know what kind of object the value is.

    private final HashMap<String, Integer> left2Right = new HashMap<String, Object>();
    left2Right.put("one", 1);
    left2Right.put("two", new Date());
    // Because the compiler cannot know that type "one" might be, this isn't allowed and     you have to cast the value. It might be anything - in this example Date or Integer.
    int numberOne = left2Right.get("one");

I hope that helps.

Both P and E seem not to be used for the input of the extractors so why are you even having them? Just return int and String and be done with it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM