Derleyici, ilişkisiz bir arabirim türüyle çağrıldığında neden bu genel yöntemi bir sınıf türü parametresiyle seçiyor?


11

Aşağıdaki iki sınıfı ve arayüzü düşünün:

public class Class1 {}
public class Class2 {}
public interface Interface1 {}

Neden ikinci çağrı için gelmez mandatoryile aşırı yöntemi çağırmak Class2, eğer getInterface1ve Interface1hiçbir ilişkisi var Class2?

public class Test {

    public static void main(String[] args) {
        Class1 class1 = getClass1();
        Interface1 interface1 = getInterface1();

        mandatory(getClass1());     // prints "T is not class2"
        mandatory(getInterface1()); // prints "T is class2"
        mandatory(class1);          // prints "T is not class2"
        mandatory(interface1);      // prints "T is not class2"
    }

    public static <T> void mandatory(T o) {
        System.out.println("T is not class2");
    }

    public static <T extends Class2> void mandatory(T o) {
        System.out.println("T is class2");
    }

    public static <T extends Class1> T getClass1() {
        return null;
    }

    public static <T extends Interface1> T getInterface1() {
        return null;
    }
}

Java 8'in Java 7 ile uyumluluğu bozduğunu anlıyorum :

$ /usr/lib/jvm/java-8-openjdk-amd64/bin/javac -source 1.7 -target 1.7 *java; /usr/lib/jvm/java-8-openjdk-amd64/bin/java Test
warning: [options] bootstrap class path not set in conjunction with -source 1.7
1 warning
T is not class2
T is not class2
T is not class2
T is not class2

Java 8 ile (11 ve 13 ile de test edilmiştir):

$ /usr/lib/jvm/java-8-openjdk-amd64/bin/javac *java; /usr/lib/jvm/java-8-openjdk-amd64/bin/java Test                        
T is not class2
T is class2
T is not class2
T is not class2

1
Alt satır: Java'da aşırı yükleme yöntemi çok fazla sürpriz getiriyor, sadece aşırı dikkatle kullanılmalıdır. İki aşırı yükü sadece bir tip parametresinin bir sınırı ile ayırmak, cevabın karmaşıklığı ile gösterildiği gibi sorun istemektedir. Temel olarak, kodunuzun her okuyucusunun kodunuzu anlamadan önce bu cevabı okumasını ve anlamasını istersiniz. Başka bir deyişle, tür çıkarımı iyileştirildiğinde programınız bozulursa, güvenli bir bölgede değilsinizdir. İyi şanslar!
Stephan Herrmann

Yanıtlar:


4

Tür çıkarım kuralları Java 8'de önemli bir revizyon aldı, en önemlisi hedef tür çıkarım çok gelişti. Bu nedenle, Java 8'den önce, yöntem bağımsız değişken sitesi varsayılan olarak silinmiş türe ( ve for ) Class1yönelik herhangi bir çıkarım almazken , Java 8'de en spesifik uygulanabilir tür çıkarılır. Java 8 için JLS, Bölüm 18'de yeni bir bölüm tanıttı . Java 7 için JLS'de eksik Tür Çıkarım .getClass1()Interface1getInterface1()


En uygun spesifik türü <T extends Interface1>olup <X extends RequiredClass & BottomInterface>burada, RequiredClassbir bağlama için gerekli bir sınıftır ve BottomInterface(dahil olmak üzere tüm arabirimler için bir alt türü Interface1).

Not: Her Java türü olarak temsil edilebilir SomeClass & SomeInterfaces. Yana RequiredClassalt türüdür SomeClassve BottomInterfacealt türüdür SomeInterfaces, Xher Java tip alt türüdür. Bu nedenle, Xbir Java alt türüdür.

Xhem maçları public static <T> void mandatory(T o)ve public static <T extends Class2> void mandatory(T o)beri yöntemler imzaları XJava alt türüdür.

Yani, uygun §15.12.2 , mandatory(getInterface1())en belirgin aşırı yüklenmesini çağırır mandatory()olan yöntemde, public static <T extends Class2> void mandatory(T o)çünkü <T extends Class2>daha spesifiktir <T>.

getInterface1()Type parametresini, public static <T extends Class2> void mandatory(T o)yöntem imzasıyla eşleşen sonucu döndürmesi için açıkça belirtebilirsiniz :

public static <T extends Class2 & Interface1> void helper() {
    mandatory(Test.<T>getInterface1()); // prints "T is class2"
}

En uygun spesifik tip <T extends Class1>IS <Y extends Class1 & BottomInterface>, nerede BottomInterfacetüm arabirimler için bir alt türüdür.

Ypublic static <T> void mandatory(T o)yöntem imzasıyla eşleşir , ancak genişlemediğinden public static <T extends Class2> void mandatory(T o)yöntem imzasıyla eşleşmez .YClass2

Yani yöntemi mandatory(getClass1())çağırır public static <T> void mandatory(T o).

Bunun aksine getInterface1(), getClass1()type parametresini public static <T extends Class2> void mandatory(T o)yöntem imzasıyla eşleşen sonucu döndürmesi için açıkça belirtemezsiniz :

                       java: interface expected here
                                     
public static <T extends Class1 & C̲l̲a̲s̲s̲2> void helper() {
    mandatory(Test.<T>getClass1());
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.