简体   繁体   English

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

[英]Redesigning around unchecked cast warnings

I have a class will contain a few different parser implementations for different objects. 我有一个类将包含一些针对不同对象的不同解析器实现。 While I am able to store the parser implementations without any warnings, getting a parser from the map warns about an unchecked cast exception. 虽然我能够在没有任何警告的情况下存储解析器实现,但是从地图获取解析器会警告未经检查的强制转换异常。 Below is a simplified excerpt: 以下是简化的摘录:

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);
}

Is there another way to implement similar logic without causing an unchecked cast warning? 是否有其他方法可以实现类似的逻辑而不会导致未经检查的强制警告?

Consider using a TypeToInstanceMap<Parser<?>> from Google Guava. 考虑使用Google Guava中的TypeToInstanceMap<Parser<?>> This will let you do things like this with no compiler warnings or errors whatsoever: 这将让你做这样的事情,没有任何编译器警告或错误:

TypeToInstanceMap<Parser<?>> parsers;

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

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

This is essentially a library that does something very similar to @ruakh's answer under the hood. 这本质上是一个库,它与@ ruakh的答案非常类似。

The developer who added the <T> to Class<T> , Neil Gafter, discussed the fundamental issue in his blog shortly after Java 5 was released. 在Java 5发布后不久, <T>添加到Class<T>的开发人员Neil Gafter 在他的博客中讨论了基本问题 He calls Class<T> a "type token", and says: 他将Class<T>称为“类型令牌”,并说:

[Y]ou simply can't make a type token for a generic type [Y]你根本无法为泛型类型创建类型令牌

... in other words, you can't make a Class<Parser<T>> . ...换句话说,你不能创建一个Class<Parser<T>>

There's no way to create a Map<Class<...>, Parser<...>> where the ... -s can both be anything but have to match between a key and its value; 没有办法创建Map<Class<...>, Parser<...>> ,其中... -s可以是任何东西,但必须在键和它的值之间匹配; so there's no way that you can get the compiler to do the checking for you, where retrieving a Class<T> is guaranteed to give you a Parser<T> . 所以你无法让编译器为你做检查,在那里检索Class<T>可以保证给你一个Parser<T> However, your code itself is correct; 但是,您的代码本身是正确的; you know that your cast is correct, even though the compiler does not. 知道你的演员是正确的,即使编译器没有。

So, when you know that your cast is correct, but Java doesn't know it, what can you do? 所以,当你知道你的演员是正确的,但Java不知道,你能做什么?

The best and safest approach is to craft a specific piece of your code, as small as possible, that is responsible for handling the translation between checked and unchecked logic, and for making sure that the unchecked logic doesn't cause any mistakes. 最好和最安全的方法是制作一个尽可能小的代码的特定部分,负责处理已检查和未检查逻辑之间的转换,并确保未经检查的逻辑不会导致任何错误。 Then, you just mark that code with the appropriate @SuppressWarnings annotation. 然后,您只需使用相应的@SuppressWarnings注释标记该代码。 For example, you can have something like this: 例如,你可以这样:

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 ...");
        }
    }
}

This would allow you to safely write, in your example: 在您的示例中,这将允许您安全地编写:

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);
}

Since your map parsers value type is Parser<?> and your method's return type is Parser<T> , it's clearly an error to cast the result of parsers.get(type) to T . 由于您的map parsers值类型是Parser<?>而您的方法的返回类型是Parser<T> ,因此将parsers.get(type)的结果parsers.get(type)T显然是错误的。

One way to remove compile error is to cast the type to Parser<T> : 删除编译错误的一种方法是将类型转换为Parser<T>

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

Also, you can change the return type to Parser<?> since you specified the parsers map as Map<Class<?>, Parser<?>> . 此外,您可以将返回类型更改为Parser<?>因为您将解析器映射指定为Map<Class<?>, Parser<?>> This will clear the compilation error, too. 这也将清除编译错误。

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

Or you can add type parameter to your wrapping class. 或者您可以将类型参数添加到包装类。

public class YourClass<T> {
  private Map<Class<T>, Parser<T>> parsers = new HashMap<>();

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

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

I'm not sure which one can be correctly applied, however, try not to use type casting. 我不确定哪一个可以正确应用,但是,尽量不要使用类型转换。 Consider why we use generic. 考虑为什么我们使用泛型。

I got this to work in a different way. 我让它以不同的方式工作。 I am experimenting with generics myself and would be happy to receive criticism :) 我自己正在试验仿制药,很乐意接受批评:)

What I did was add a tagging interface for Parseable objects and then use that as an upper bound on the Parser. 我所做的是为Parseable对象添加标记接口,然后将其用作Parser的上限。

public interface IParseable {}

public class Parser<T extends IParseable> {
    T paresableObj;
    // do something with parseableObject here
}

And now the Parser Factory does not have to use wildcards nor use casts. 现在,Parser Factory不必使用通配符也不必使用强制转换。

public class ParserFactory {

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

    public <T> void addParser(Class<T> type, Parser<? extends IParseable> parser) {
        if(parserFor(type) == null){
            parsers.put(type, parser);
        }else{
            //throw some excep
        }
    }

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

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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