重新设计未经检查的演员警告

问题描述 投票:4回答:4

我有一个类将包含一些针对不同对象的不同解析器实现。虽然我能够在没有任何警告的情况下存储解析器实现,但是从地图获取解析器会警告未经检查的强制转换异常。以下是简化的摘录:

private Map<Class<?>, Parser<?>> parsers = new HashMap<>();

public <T> void addParser(Class<T> type, Parser<T> parser) {
    parsers.put(type, parser);
}

private <T> Parser<T> parserFor(Class<T> type) {
    // Compiler complains about unchecked cast below
    return (Parser<T>) parsers.get(type);
}

是否有其他方法可以实现类似的逻辑而不会导致未经检查的强制警告?

java generics
4个回答
2
投票

没有办法创建一个Map<Class<...>, Parser<...>>,其中...-s可以是任何东西,但必须在键和它的值之间匹配;所以你无法让编译器为你做检查,在那里检索Class<T>可以保证给你一个Parser<T>。但是,您的代码本身是正确的;你知道你的演员是正确的,即使编译器没有。

所以,当你知道你的演员是正确的,但Java不知道,你能做什么?

最好和最安全的方法是制作一个尽可能小的代码的特定部分,负责处理已检查和未检查逻辑之间的转换,并确保未经检查的逻辑不会导致任何错误。然后,您只需使用适当的@SuppressWarnings注释标记该代码。例如,你可以这样:

public abstract class Parser<T> {
    private final Class<T> mType;

    protected Parser(final Class<T> type) {
        this.mType = type;
    }

    public final Class<T> getType() {
        return mType;
    }

    @SuppressWarnings("unchecked")
    public final <U> Parser<U> castToParserOf(final Class<U> type) {
        if (type == mType) {
            return (Parser<U>) this;
        } else {
            throw new ClassCastException("... useful message ...");
        }
    }
}

在您的示例中,这将允许您安全地编写:

public <T> void addParser(final Parser<T> parser) {
    parsers.put(parser.getType(), parser);
}

private <T> Parser<T> parserFor(final Class<T> type) {
    return parsers.get(type).castToParserOf(type);
}

2
投票

考虑使用Google Guava的TypeToInstanceMap<Parser<?>>。这将让你做这样的事情,没有任何编译器警告或错误:

TypeToInstanceMap<Parser<?>> parsers;

parsers.putInstance(new TypeToken<Parser<String>>(){},
                    makeStringParser());

Parser<Integer> intParser = parsers.getInstance(new TypeToken<Parser<Integer>>(){});

这本质上是一个库,可以做一些与@ruakh's answer非常相似的东西。

在Java 5发布后不久,将qazxsw poi添加到qazxsw poi,Neal Gafter,qazxsw poi的开发人员。他称<T>为“类型代币”,并说:

[Y]你根本无法为泛型类型创建类型令牌

...换句话说,你不能做一个Class<T>


1
投票

由于您的地图discussed the fundamental issue in his blog值类型为Class<T>且您的方法的返回类型为Class<Parser<T>>,因此将parsers的结果强制转换为Parser<?>显然是错误的。

删除编译错误的一种方法是将类型强制转换为Parser<T>

parsers.get(type)

此外,您可以将返回类型更改为T,因为您将解析器映射指定为Parser<T>。这也将清除编译错误。

private <T> Parser<T> parserFor(Class<T> type) {
  return (Parser<T>)parsers.get(type);
}

或者您可以将类型参数添加到包装类。

Parser<?>

我不确定哪一个可以正确应用,但是,尽量不要使用类型转换。考虑为什么我们使用泛型。


0
投票

我让它以不同的方式工作。我自己正在试验仿制药,很乐意接受批评:)

我所做的是为Parseable对象添加标记接口,然后将其用作Parser的上限。

Map<Class<?>, Parser<?>>

现在,Parser Factory不必使用通配符也不必使用强制转换。

private <T> Parser<?> parserFor(Class<T> type) {
  return parsers.get(type);
}
© www.soinside.com 2019 - 2024. All rights reserved.