英文:
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
)。
有没有办法实现这一点?我甚至可以在编译器设置中将这种问题从错误变为警告,或者可能有一个额外的私有方法,在单个对象中同时返回config
和result
?
编辑:为了进一步解释“进一步的编译问题”,如果我使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<T> {
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<?> computable;
Object config; // Real type is <?>
int result;
ComputableInfo(String id) {
computable = ComputableFactory.createFrom(id);
config = computable.configure();
result = computable.compute(config); // <<<--- The method compute(capture#3-of ?) in the type TestInterface.IComputable<capture#3-of ?> 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<T> {
IComputable<T> 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<?> info = getInfo(id);
info.computable.precision(info.config); // <<<--- (same kind of error)
The problem is that the ComputableInfo
has no way to know the T
type of Computable<T>
(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<?> 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 <T> void copyFromTo(List<T> l, int from, int to) {
l.set(to, l.get(from));
}
List<?> list = …
copyFromTo(list, 0, 1); // now works
You can apply this pattern to your case as well:
class ComputableInfo {
IComputable<?> computable;
Object config; // Real type is <?>
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);
}
}
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<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();
}
}
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; // 可以
然而,通过一个实例的方法安全地传递 Integer
或 Double
是安全的。在下面的代码片段中,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<? super Object> 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<Integer> computableInteger = ...;
IComputable<Number> computableNumber = ...;
IComputable<Object> computableObject = ...;
IComputable<? super Number> computableSuperNumber = ...;
computableSuperNumber = computableInteger; // doesn'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<Number>
IComputable<Object>
.
Since the reference might be IComputable<Number>
, computing with Object
is illegal as long it doesn't fit there as long as Object
can be ex. String
.
IComputable<? super Number> computableSuperNumber = ...;
Integer integer = 1;
Double d = 1d;
Number number = 1;
Object object = 1; // the Object can be also "string", see below
Object objectString = "string";
String string = "string";
computableSuperNumber.compute(integer); // ok
computableSuperNumber.compute(d); // ok
computableSuperNumber.compute(number); // ok
computableSuperNumber.compute(object); // doesn't compile
computableSuperNumber.compute(objectString); // doesn't compile
computableSuperNumber.compute(string); // doesn't compile
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论