错误的关键在于F 类型的泛型声明:F extends Function<T, R>。不起作用的语句是:new Builder<MyInterface>().with(MyInterface::getNumber, 4L); 首先,你有一个新的Builder<MyInterface>。因此类的声明暗示T = MyInterface。根据您对with 的声明,F 必须是Function<T, R>,在这种情况下是Function<MyInterface, R>。因此,参数getter必须以MyInterface为参数(通过方法引用MyInterface::getNumber和MyInterface::getLong满足),并返回R,该R必须与函数@的第二个参数类型相同987654338@。现在,让我们看看这是否适用于您的所有情况:
// T = MyInterface, F = Function<MyInterface, Long>, R = Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L explicitly widened to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().<Function<MyInterface, Number>, Number>with(MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Long
// F = Function<T, not R> violates definition, therefore compilation error occurs
// Compiler cannot infer type of method reference and 4L at the same time,
// so it keeps the type of 4L as Long and attempts to infer a match for MyInterface::getNumber,
// only to find that the types don't match up
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
您可以使用以下选项“修复”此问题:
// stick to Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// stick to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// explicitly convert the result of getNumber:
new Builder<MyInterface>().with(myInstance -> (Long) myInstance.getNumber(), 4L);
// explicitly convert the result of getLong:
new Builder<MyInterface>().with(myInterface -> (Number) myInterface.getLong(), (Number) 4L);
除此之外,对于哪个选项可以降低特定应用程序的代码复杂性,主要是一个设计决策,因此请选择最适合您的选项。
不进行强制转换就无法做到这一点的原因在于from the Java Language Specification:
装箱转换将原始类型的表达式视为相应引用类型的表达式。具体来说,以下九种转换称为装箱转换:
- 从布尔类型到布尔类型
- 从字节类型到字节类型
- 从短类型到短类型
- 从 char 类型到 Character 类型
- 从 int 类型到 Integer 类型
- 从 long 类型到 Long 类型
- 从浮点型到浮点型
- 从 double 类型到 Double 类型
- 从空类型到空类型
您可以清楚地看到,从 long 到 Number 没有隐式装箱转换,并且从 Long 到 Number 的扩大转换只能在编译器确定它需要 Number 而不是 Long 时发生。由于需要 Number 的方法引用与提供 Long 的 4L 之间存在冲突,因此编译器(出于某种原因???)无法进行 Long 是 Number 的逻辑飞跃并推断出 F 是Function<MyInterface, Number>。
相反,我设法通过稍微编辑函数签名来解决问题:
public <R> Builder<T> with(Function<T, ? super R> getter, R returnValue) {
return null;//TODO
}
此更改后,将发生以下情况:
// doesn't work, as it should not work
new Builder<MyInterface>().with(MyInterface::getLong, (Number), 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works, as it should work
new Builder<MyInterface>().with(MyInterface::getNumber, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
编辑:
在花费了更多时间之后,实施基于 getter 的类型安全变得非常困难。这是一个使用 setter 方法来强制构建器的类型安全的工作示例:
public class Builder<T> {
static public interface MyInterface {
//setters
void number(Number number);
void Long(Long Long);
void string(String string);
//getters
Number number();
Long Long();
String string();
}
// whatever object we're building, let's say it's just a MyInterface for now...
private T buildee = (T) new MyInterface() {
private String string;
private Long Long;
private Number number;
public void number(Number number)
{
this.number = number;
}
public void Long(Long Long)
{
this.Long = Long;
}
public void string(String string)
{
this.string = string;
}
public Number number()
{
return this.number;
}
public Long Long()
{
return this.Long;
}
public String string()
{
return this.string;
}
};
public <R> Builder<T> with(BiConsumer<T, R> setter, R val)
{
setter.accept(this.buildee, val); // take the buildee, and set the appropriate value
return this;
}
public static void main(String[] args) {
// works:
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::number, (Number) 4L);
// compile time error, as it shouldn't work
new Builder<MyInterface>().with(MyInterface::Long, (Number) 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works, as it should
new Builder<MyInterface>().with(MyInterface::number, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::number, 4L);
// compile time error, as you wanted
new Builder<MyInterface>().with(MyInterface::number, "blah");
}
}
提供了构造对象的类型安全能力,希望在未来的某个时候我们能够从构建器返回一个immutable data object(也许通过向接口添加一个toRecord()方法,并指定builder 为Builder<IntermediaryInterfaceType, RecordType>),因此您甚至不必担心生成的对象会被修改。老实说,要获得一个类型安全的字段灵活构建器需要付出如此多的努力,这绝对是一种耻辱,但如果没有一些新功能、代码生成或烦人的反射量,这可能是不可能的。