Hatanızla anahtarı tipi jenerik bildiriminde olduğunu F
: F extends Function<T, R>
. Çalışmayan ifade: new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
İlk önce bir yeniniz var Builder<MyInterface>
. Bu nedenle sınıfın ilanı ima eder T = MyInterface
. Beyanınıza göre with
, bu durumda F
bir Function<T, R>
, bir olmalıdır Function<MyInterface, R>
. Bu nedenle, parametre getter
bir MyInterface
as parametresini (yöntem başvuruları MyInterface::getNumber
ve memnuniyeti tarafından karşılanır MyInterface::getLong
) R
almalı ve işleve ikinci parametre ile aynı türde olması gereken döndürmelidir with
. Şimdi, bunun tüm davalarınız için geçerli olup olmadığını görelim:
// 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);
Bu sorunu aşağıdaki seçeneklerle "düzeltebilirsiniz":
// 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);
Bu noktanın ötesinde, çoğunlukla hangi seçeneğin uygulamanız için kod karmaşıklığını azalttığı için bir tasarım kararıdır, bu nedenle size en uygun olanı seçin.
Döküm yapmadan bunu yapamamanızın nedeni , Java Dil Spesifikasyonu'ndan aşağıdadır :
Boks dönüşümü, ilkel tipteki ifadeleri, karşılık gelen bir referans tipinin ifadeleri olarak kabul eder. Özellikle, aşağıdaki dokuz dönüşüme boks dönüşümleri denir :
- Boolean türünden Boolean türüne
- Tip baytından bayt tipine
- Kısa tipten Kısa tipe
- Karakter türünden Karakter türüne
- İnt türünden Integer türüne
- Uzun tipten Uzun tipe
- Şamandıra tipinden Şamandıra tipine
- Çift tipten Çift tipe
- Null tipinden null tipine
Açıkça görebileceğiniz gibi, uzuntan Sayıya örtülü bir boks dönüşümü yoktur ve Uzuntan Sayıya genişletme dönüşümü yalnızca derleyici bir Uzun değil, bir Sayı gerektirdiğinden emin olduğunda gerçekleşebilir. Bir sayı gerektiren yöntem başvurusu ile Long sağlayan 4L arasında bir çakışma olduğu için, derleyici (bazı nedenlerden dolayı ???) Long'un bir Sayı olduğu mantıksal sıçramasını yapamaz ve bunun F
bir sonucudur Function<MyInterface, Number>
.
Bunun yerine, işlev imzasını hafifçe düzenleyerek sorunu çözmeyi başardım:
public <R> Builder<T> with(Function<T, ? super R> getter, R returnValue) {
return null;//TODO
}
Bu değişiklikten sonra aşağıdakiler gerçekleşir:
// 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);
Düzenleme:
Üzerinde biraz daha zaman geçirdikten sonra, alıcı tabanlı tip güvenliğini zorlamak can sıkıcı bir şekilde zordur. Aşağıda, bir üreticinin tip güvenliğini zorunlu kılmak için ayarlayıcı yöntemlerini kullanan çalışan bir örnek verilmiştir:
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");
}
}
Bir nesneyi inşa etmek için güvenli bir özellik sağlandığında, umarım gelecekte bir noktada yapıcıdan değiştirilemez bir veri nesnesi döndürebiliriz (belki toRecord()
arabirime bir yöntem ekleyerek ve oluşturucuyu a olarak belirterek Builder<IntermediaryInterfaceType, RecordType>
), böylece ortaya çıkan nesnenin değiştirildiği konusunda endişelenmenize bile gerek kalmaz. Dürüst olmak gerekirse, bir tür güvenli alan esnek oluşturucu elde etmek için çok çaba gerektirmesi mutlak bir utançtır, ancak bazı yeni özellikler, kod üretimi veya sinir bozucu bir yansıma olmadan muhtemelen imkansızdır.
MyInterface
?