Parametrelerde Java tipi tanıtımı


20

Bu pasajı tökezledim:

public class ParamTest {
    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Bu derleme hatasına neden olacaktır:

Hata: (15, 9) java: printSum referansı ParamTest'te hem printSum (int, double) yöntemi hem de ParamTest eşleşmesinde printSum (uzun, uzun) yöntemi belirsiz

Bu nasıl belirsiz? İlk parametre zaten int olduğundan bu durumda yalnızca ikinci parametre yükseltilmemelidir? Bu durumda ilk parametrenin desteklenmesi gerekmez, değil mi?

Başka bir yöntem eklemek için kodu güncelleştirirseniz derleme başarılı:

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

Sadece açıklığa kavuşturmak için genişleyeyim. Aşağıdaki kod belirsizliğe neden olur:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Daha sonra aşağıdaki bu kod da belirsizliğe neden olur:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Ancak bu bir değil belirsizlik neden:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, double b) {
        System.out.println("In longDBL " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

2
Derleyici çağrınızı eşleyebilir printSum (1, 2); printSum (uzun a, uzun b) veya printSum (int a, çift b) için belirsizdir. Tam olarak böyle yazarak derleyicinin aralarından seçim yapmasına açıkça yardım etmelisiniz: printSum (1, 2d)
Ravindra Ranwala

6
Hata mesajını yanlış kullandınız ve fark çok önemli. Asıl hata mesajı şudur: Error:(15, 9) java: reference to printSum is ambiguous both method printSum(int,double) in ParamTest and method printSum(long,long) in ParamTest match- belirsiz olan yöntem değil, belirsiz olan yöntemin çağrısıdır.
Erwin Bolwidt

1
@ErwinBolwidt orada tutulma özür dilerim hata mesajı oldu. neyse, hala anlamıyorum çünkü printSum (int a, long b) eklemek hata mesajını kaldırıyor.
riruzen

2
JLS-5.3 => İfadenin türü, gevşek bir çağırma bağlamında izin verilen bir dönüştürme ile parametre türüne dönüştürülemezse, derleme zamanı hatası oluşur. içeriğe uygulanabilir gibi gözüküyor, ancak bunu nasıl anlayacağımız çok açık değil. +1
Naman

1
Bunun için kesinlikle standart bir soruya ihtiyacımız var: stackoverflow.com/… . "
Marco13

Yanıtlar:


17

Bunun JLS'nin 15.12.2.5 ile ilgili özel kuralı ile ilgili bir şey olduğunu düşünüyorum . En Özel Yöntemi Seçmek . Şu hususları belirtmektedir:

Bir yöntem çağrısına hem birden fazla üye yöntemi erişilebilir hem de uygulanabilirse, çalışma zamanı yöntemi gönderimi için tanımlayıcı sağlamak üzere bir yöntem seçmek gerekir. Java programlama dili, en özel yöntemin seçildiği kuralı kullanır.

Java'nın en spesifik yöntemi nasıl seçtiği metin tarafından daha ayrıntılı olarak açıklanmaktadır:

Resmi olmayan sezgi, birinci yöntemle işlenen herhangi bir çağırma derleme zamanı hatası olmadan diğerine geçirilebiliyorsa, bir yöntemin diğerinden daha spesifik olmasıdır. Açıkça yazılmış bir lambda ifade argümanı (§15.27.1) veya değişken bir çağırma çağrısı (§15.12.2.4) gibi durumlarda, bir imzanın bir imzayı diğerine uyarlamasına izin verilir.

Örneğinizde, tüm yöntemlere erişilebilir ve yöntem çağırma için uygulanabilir, bu nedenle Java'nın hangisinin en spesifik olduğunu belirlemesi gerekir .

Bu yöntemler için hiçbirinin daha spesifik olduğu tespit edilemez:

public static void printSum(int a, double b) {
    System.out.println("In intDBL " + (a + b));
} // int, double cannot be passed to long, long or double, long without error

public static void printSum(long a, long b) {
    System.out.println("In long " + (a + b));
} // long , long cannot be passed to int, double or double, long without error

public static void printSum(double a, long b) {
    System.out.println("In doubleLONG " + (a + b));
} // double, long cannot be passed to int, double or long, long without error

Dördüncü yöntem belirsizliği tam olarak temizler çünkü en spesifik olmak için gerekli koşulu yerine getirir .

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

Yani, (int, long) derleme hataları olmadan (int, double), (long, long) veya (double, long) 'e geçirilebilir.


2
Yazmak için, tüm yöntemlere çağrı tarafından erişilebilirse, java derleme zamanı hatası olmadan diğer yöntemlere çağrılabilecek yöntemi tanımlar, eğer bulunursa, o zaman en spesifik olarak kullanın ve aksi takdirde belirsizlik hatası olarak mı adlandırılır? Bence, bu cevap @ riruzen.
Sandeep Kokate

Teşekkürler! Bunu (double, int) vs (double, long) ile denedim ve gerçekten belirsizliği temizledi, sonra (double, int) vs (long, double) beklendiği gibi belirsizdi.
riruzen

7

Gerçekten çok ilginç bir soru. Java Dil Belirtimini adım adım inceleyelim.

  1. Derleyici potansiyel olarak uygulanabilir yöntemleri tanımlamaya çalışırken, yaptığı ilk şey Sıkı Çağırma tarafından uygulanabilir yöntemler için serching yapmaktır .

  2. Sizin durumunuzda böyle bir yöntem yoktur, bu nedenle bir sonraki adım Gevşek Invocation tarafından uygulanabilir yöntemleri bulmaktır

  3. Bu noktada tüm yöntemler eşleşir, bu nedenle en özel yöntem ( §15.12.2.5 ) gevşek çağırma ile uygulanabilir yöntemler arasından seçilir.

Bu kilit bir andır, bu yüzden buna yakından bakalım.

Aşağıdakilerden herhangi biri doğruysa, m1 yöntemlerinden biri, e1, ..., ek argüman ifadeleriyle bir çağrı için uygulanabilir başka bir yöntem m2'den daha spesifiktir:

(Sadece aşağıdaki durumla ilgileniyoruz):

  • m2 genel değildir ve m1 ve m2 katı veya gevşek çağrı ile uygulanabilir ve m1'in resmi parametre türleri S1, ..., Sn ve m2'nin resmi parametre türleri T1, ..., Tn ise, Si tipi daha fazladır tüm i için ei argümanı için Ti'den spesifik (1 ≤ i ≤ n, n = k).

Basitçe söylemek gerekirse, bir yöntem daha spesifik ise tüm parametre türleri daha spesifiktir . Ve

S <: T ( §4.10 ).

İfade S <: Tbunun Sbir alt türü olduğu anlamına gelir T. İlkeller için aşağıdaki ilişkiye sahibiz:

double > float > long > int

Öyleyse yöntemlerinize bakalım ve hangisinin diğerlerinden daha spesifik olduğunu görelim.

public static void printSum(int a, double b) {  // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(double a, long b) { // method 2
    System.out.println("In doubleLONG " + (a + b));
}

Bu örnekte, yöntem 1'in ilk parametresi açıkça yöntem 2'nin ilk parametresinden daha belirgindir (tamsayı değerleriyle çağırırsanız:) printSum(1, 2). Ancak ikinci parametre yöntem 2 için daha spesifiktir , çünkü long < double. Yani bu yöntemlerin hiçbiri diğerinden daha belirgin değil. Bu yüzden burada bir belirsizlik var.

Aşağıdaki örnekte:

public static void printSum(int a, double b) { // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(long a, double b) { // method 2
    System.out.println("In longDBL " + (a + b));
}

yöntem 1'in birinci parametre tipi, yöntem 2'dekinden daha spesifiktir, çünkü int < longikinci parametre tipi her ikisi için de aynıdır, bu yüzden yöntem 1 seçilir.


Cevabınızı küçümsemedim, ancak bu cevap (int, double) 'un neden (uzun, uzun) ile belirsiz olduğunu açıklamıyor, çünkü açıkça (int, double) ilk parametreye göre daha spesifik olmalı.
riruzen

3
@riruzen açıklıyor: doubledaha spesifik değil long. Ve seçilecek yöntem için tüm tip parametreleri daha spesifik olmalıdır: Si tipi, tüm i için ei argümanı için Ti'den daha spesifiktir (1 ≤ i ≤ n, n = k)
Kirill Simonov

+1 kadar olmalı
Çay

0

çünkü int değeri java'da çift olarak düşünülebilir. araç double a = 3geçerli ve uzun ile aynıdır long b = 3Bu yüzden bir belirsizlik yaratır. Sen ara

printSum(1, 2);

Her üç yöntem için de kafa karıştırıcıdır, çünkü bu üç yöntem de geçerlidir:

int a = 1;
double b =1;
long c = 1;

Uzun değeri belirtmek için L'yi sonuna koyabilirsiniz. Örneğin:

printSum(1L, 2L);

çift ​​için dönüştürmeniz gerekir:

printSum((double)1, 2L);

@Erwin Bolwidt'in açıklamasını da okuyun


Evet derleyici tüm 3 yöntem için karıştırılır, ilk derleyici tam türü arar ve eğer bulamazsa uyumlu türü bulmaya çalışın ve bu durumda hepsi uyumludur.
Başka bir kodlayıcı

2
3 yerine 4 yöntem bildirimi varsa, belirsizlik ortaya çıkar: public static void printSum (int a, double b) public static void printSum (int a, long b) public static void printSum (long a, long b) public static void printSum (çift a, uzun b) bu ​​yüzden cevabınızı kabul ederken, yine de soruyu cevaplamıyor. Yanılmıyorsam Java, tam tür kullanılamıyorsa otomatik tür tanıtımı yapar. Neden bu 3 ile belirsizleştiğini anlamıyorum.
riruzen
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.