告诉编译器一个等同于它所需的
huangapple go评论72阅读模式
英文:

Tell compiler an <Object> is equivalent to the <?> it wants

问题

我有一些对象,它们预先生成一些配置,以便以后可以更快地处理计算(可能多次)。我试图泛化它,以避免将配置作为“Object”传递并在每次使用时进行强制转换。

interface IComputable<T> {
    T configure(); // 生成配置对象
    int compute(T conf); // 根据预先生成的配置运行计算
    float precision(T conf); // 使compute()计算更精细
    ...
}
class ComputableInfo {
    IComputable<?> computable;
    Object config; // 实际类型是<?>
    int result;

    ComputableInfo(String id) {
        computable = ComputableFactory.createFrom(id);
        config = computable.configure();
        result = computable.compute(config); // <<<--- 在类型TestInterface.IComputable<capture#3-of ?>中,方法compute(capture#3-of ?)不适用于参数(Object)
    }
}

我得到了编译错误:

在类型TestInterface.IComputable<capture#3-of ?>中,方法compute(capture#3-of ?)不适用于参数(Object)

当然,我可以将int compute(T conf)替换为int compute(Object conf),但我必须显式地将其强制转换为适当的T类型。这不是一个大问题,但它会使代码变得不太明显。

我还可以通过以下方式使ComputableInfo泛型化:

interface ComputableInfo<T> {
    IComputable<T> computable;
    T config;
    ...

但这将在一些其他地方引起编译问题(主要是“原始类型”警告),我希望避免比之前的解决方法更多的问题(在T的地方使用Object而不是T)。

有没有办法实现这一点?我甚至可以在编译器设置中将这种问题从错误变为警告,或者可能有一个额外的私有方法,在单个对象中同时返回configresult

编辑:为了进一步解释“进一步的编译问题”,如果我使ComputableInfo泛型化:我在接口中还有另一个方法(请参见编辑),通过ComputableInfo调用:

ComputableInfo<?> info = getInfo(id);
info.computable.precision(info.config); // <<<--- (相同类型的错误)

问题是ComputableInfo无法知道Computable<T>T类型(至少我不知道),因为它来自一个从配置文件构建它的工厂。

英文:

I have objects that pre-generate some configuration once so they can process computation later on faster (possibly several times). I'm trying to genericize it to avoid passing the configuration as an Object and casting it each time.

interface IComputable&lt;T&gt; {
    T configure(); // Generate configuration object
    int compute(T conf); // Run the computation based on the pre-generated configuration
    float precision(T conf); // Make the compute() computation finer-grained
    ...
}
class ComputableInfo {
    IComputable&lt;?&gt; computable;
    Object config; // Real type is &lt;?&gt;
    int result;

    ComputableInfo(String id) {
        computable = ComputableFactory.createFrom(id);
        config = computable.configure();
        result = computable.compute(config); // &lt;&lt;&lt;--- The method compute(capture#3-of ?) in the type TestInterface.IComputable&lt;capture#3-of ?&gt; is not applicable for the arguments (Object)
    }
}

I'm getting a compilation error:
> The method compute(capture#3-of ?) in the type TestInterface.IComputable<capture#3-of ?> is not applicable for the arguments (Object)

Of course, I can replace the int compute(T conf) by int compute(Object conf) but I would have to explicitly cast it to the appropriate T. It's not that big a problem, but it makes the code less obvious.

I could also make the ComputableInfo generic with

interface ComputableInfo&lt;T&gt; {
    IComputable&lt;T&gt; computable;
    T config;
    ...

but that would create compilation problems in some other places (mostly "raw types" warning) that I'd like to avoid more than the previous workaround (using Object instead of T).

Is there a way to achieve that? I'm even open to turning such problem from error to warning in the compiler settings, or maybe have an extra private method that would return both config and result in a single object?

EDIT: to add up on the "further compilation problems" if I make ComputableInfo generic: I have another method in the interface (see edited) that is called through the ComputableInfo:

ComputableInfo&lt;?&gt; info = getInfo(id);
info.computable.precision(info.config); // &lt;&lt;&lt;--- (same kind of error)

The problem is that the ComputableInfo has no way to know the T type of Computable&lt;T&gt; (or no way I know), as it comes from a Factory that builds it from a configuration file.

答案1

得分: 4

从通配符类型中获取对象并将其传回相同的对象,是泛型类型系统的已知限制。例如,当您有:

List<?> list = ...

您可能希望从一个索引复制一个元素到另一个索引,类似于:

Object o = list.get(0);
list.set(1, o);

但是它不起作用,即使您避免了不可指示类型的局部变量。换句话说,即使以下操作也无法编译通过:

list.set(1, list.get(0));

但是您可以添加一个泛型辅助方法,在操作的持续时间内允许捕获通配符类型到一个类型参数中,以执行该操作:

static <T> void copyFromTo(List<T> l, int from, int to) {
    l.set(to, l.get(from));
}
List<?> list = ...
copyFromTo(list, 0, 1); // 现在可以工作

您也可以将此模式应用于您的情况:

class ComputableInfo {
    IComputable<?> computable;
    Object config; // 实际类型是 <?>
    int result;

    ComputableInfo(String id) {
        computable = ComputableFactory.createFrom(id);
        configureAndCompute(computable);
    }

    private <T> void configureAndCompute(IComputable<T> computable) {
        T typedConfig = computable.configure();
        this.config = typedConfig;
        this.result = computable.compute(typedConfig);
    }
}

这样可以工作,而且不需要使 ComputableInfo 泛型。

如果您需要捕获类型的时间超过单个方法,例如,如果您想多次使用创建的 config,您可以使用封装:

class ComputableInfo {
    static final class CompState<T> {
        IComputable<T> computable;
        T config;

        CompState(IComputable<T> c) {
            computable = c;
        }
        private void configure() {
            config = computable.configure();
        }
        private int compute() {
            return computable.compute(config);
        }
    }
    CompState<?> state;
    int result;

    ComputableInfo(String id) {
        state = new CompState<>(ComputableFactory.createFrom(id));
        state.configure();
        result = state.compute();
    }
}

这样,您仍然避免了将类型参数导出给 ComputableInfo 的用户。

英文:

Getting an object from a wildcarded type and passing it back to the same object, is a known limitation of the generic type system. E.g., when you have

List&lt;?&gt; list = 

you might want to copy an element from one index to another like

Object o = list.get(0);
list.set(1, o);

but it doesn’t work, even when you avoid the local variable of a non-denotable type. In other words, even the following does not compile:

list.set(1, list.get(0));

But you can add a generic helper method that does the operation by allowing to capture the wildcard type in a type parameter for the duration of the operation:

static &lt;T&gt; void copyFromTo(List&lt;T&gt; l, int from, int to) {
    l.set(to, l.get(from));
}
List&lt;?&gt; list = 
copyFromTo(list, 0, 1); // now works

You can apply this pattern to your case as well:

class ComputableInfo {
    IComputable&lt;?&gt; computable;
    Object config; // Real type is &lt;?&gt;
    int result;

    ComputableInfo(String id) {
        computable = ComputableFactory.createFrom(id);
        configureAndCompute(computable);
    }

    private &lt;T&gt; void configureAndCompute(IComputable&lt;T&gt; computable) {
        T typedConfig = computable.configure();
        this.config = typedConfig;
        this.result = computable.compute(typedConfig);
    }
}

This works and does not require making ComputableInfo generic.

If you need to capture the type for longer than a single method, e.g. if you want to use the created config multiple times, you can use encapsulation:

class ComputableInfo {
    static final class CompState&lt;T&gt; {
        IComputable&lt;T&gt; computable;
        T config;

        CompState(IComputable&lt;T&gt; c) {
            computable = c;
        }
        private void configure() {
            config = computable.configure();
        }
        private int compute() {
            return computable.compute(config);
        }
    }
    CompState&lt;?&gt; state;
    int result;

    ComputableInfo(String id) {
        state = new CompState&lt;&gt;(ComputableFactory.createFrom(id));
        state.configure();
        result = state.compute();
    }
}

That way, you still avoid exporting the type parameter to users of ComputableInfo.

答案2

得分: 1

需要使用一个下限通配符。Object 本身与通配符 ? 不兼容。

class ComputableInfo {
    IComputable<? super Object> computable;
    Object config;
    int result;

    ComputableInfo(String id) {
         computable = null;
         config = computable.configure();
         result = computable.compute(config);
    }
}

一个下限通配符表示 IComputable 将是 Object 的实例或者是某个作为 Object 超类的对象的实例(实际上,Object 是所有 Object 的父类)。为了更好地理解,让我们使用 Number

IComputable<Integer> computableInteger = ...;
IComputable<Number> computableNumber = ...;
IComputable<Object> computableObject = ...;

IComputable<? super Number> computableSuperNumber = ...;
computableSuperNumber = computableInteger;                  // 不会编译
computableSuperNumber = computableNumber;                   // 可以
computableSuperNumber = computableObject;                   // 可以

然而,通过一个实例的方法安全地传递 IntegerDouble 是安全的。在下面的代码片段中,computableSuperObject 引用了一个 IComputable,它可以是以下之一:

  • IComputable<Number>
  • IComputable<Object>

因为引用 可能IComputable<Number>,所以使用 Object 进行计算是不合法的,只要 Object 可能是 String 之类的类型。

IComputable<? super Number> computableSuperNumber = ...;

Integer integer = 1;
Double d = 1d;
Number number = 1;
Object object = 1;                     // Object 也可以是 "string",见下面
Object objectString = "string";
String string = "string";

computableSuperNumber.compute(integer);                     // 可以
computableSuperNumber.compute(d);                           // 可以
computableSuperNumber.compute(number);                      // 可以
computableSuperNumber.compute(object);                      // 不会编译
computableSuperNumber.compute(objectString);                // 不会编译
computableSuperNumber.compute(string);                      // 不会编译

希望这些翻译对你有帮助。

英文:

You need to use a lower-bounded wildcard. The Object is not compliant with a wildcard ? itself.

class ComputableInfo {
    IComputable&lt;? super Object&gt; computable;
    Object config;
    int result;

    ComputableInfo(String id) {
         computable = null;
         config = computable.configure();
         result = computable.compute(config);
    }
}

A lower bound states that IComputable will be an instance of Object or an instance of some object being a superclass of Object (which actually the object is a parent of all Objects). For the better understanding, let's use rather Number:

IComputable&lt;Integer&gt; computableInteger = ...;
IComputable&lt;Number&gt; computableNumber = ...;
IComputable&lt;Object&gt; computableObject = ...;

IComputable&lt;? super Number&gt; computableSuperNumber = ...;
computableSuperNumber = computableInteger;                  // doesn&#39;t compile
computableSuperNumber = computableNumber;                   // ok
computableSuperNumber = computableObject;                   // ok

However, it is safe to pass an Integer or Double into a method of that instance through. In a snippet below computableSuperObject references an IComputable that could be one of:

  • IComputable&lt;Number&gt;
  • IComputable&lt;Object&gt;.

Since the reference might be IComputable&lt;Number&gt;, computing with Object is illegal as long it doesn't fit there as long as Object can be ex. String.

IComputable&lt;? super Number&gt; computableSuperNumber = ...;

Integer integer = 1;
Double d = 1d;
Number number = 1;
Object object = 1;                     // the Object can be also &quot;string&quot;, see below
Object objectString = &quot;string&quot;;
String string = &quot;string&quot;;

computableSuperNumber.compute(integer);                     // ok
computableSuperNumber.compute(d);                           // ok
computableSuperNumber.compute(number);                      // ok
computableSuperNumber.compute(object);                      // doesn&#39;t compile
computableSuperNumber.compute(objectString);                // doesn&#39;t compile
computableSuperNumber.compute(string);                      // doesn&#39;t compile

huangapple
  • 本文由 发表于 2020年9月17日 17:32:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/63935181.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定

  • 开发者交流平台

    本页二维码