Soru Genel atma cümleleri ile kuzudas ve fonksiyonel arayüzler


Java 8 kodunun bu snippet'ini düşünün:

public class Generics {
  public static <V, E extends Exception> V f(CheckedCallable1<V, E> callable) throws E {
    return callable.call();
  }
  public static <V, E extends Exception> V g(CheckedCallable2<V, E> callable) throws E {
    return callable.call();
  }
  public static void main(String[] args) {
    f(() -> 1);
    g(() -> 1);
  }
}

interface Callable<V> {
  V call() throws Exception;
}

interface CheckedCallable1<V, E extends Exception> {
  V call() throws E;
}

interface CheckedCallable2<V, E extends Exception> extends Callable<V> {
  @Override V call() throws E;
}

Çağrıda lambda f çağrılırken lambda, iyi derler g derleme değil, bu derleme hatasını verir:

Error:(10, 7) java: call() in <anonymous Generics$> cannot implement call() in CheckedCallable2
  overridden method does not throw java.lang.Exception

Bu neden?

Bana öyle geliyor ki CheckedCallable1.call ve CheckedCallable2.call yöntemler eşdeğerdir: tip silme kurallarına göre, V olur Object sınırsız olduğu gibi, ve E olur ExceptionBu, üst tip bağlı olduğu gibi. Peki derleyici neden geçersiz kılınmış yöntem java.lang.Exception atmıyor?

Derleme süresinde gerçekleşmesi muhtemel olması muhtemel olan tip silmeyi göz ardı etmese bile, yine de bana mantıklı gelmiyor: Bu kalıbın izin verilirse, neden olmasın, neden olmasın diye bir sebep göremiyorum. java kodu.

Birisi bana neden izin verilmediğine dair beni aydınlatabilir mi?

Güncelleştirme:

Bu yüzden belki daha ilginç bir şey buldum. Yukarıdaki dosyayı alın, her bir oluşumunu değiştirin Exception için IOException ve ekleme maddesini ekle main. Derleme işleri! Olarak değiştir Exception: derleme araları!

Bu iyi derlenir:

import java.io.IOException;

public class Generics {
  public static <V, E extends IOException> V f(CheckedCallable1<V, E> callable) throws E {
    return callable.call();
  }
  public static <V, E extends IOException> V g(CheckedCallable2<V, E> callable) throws E {
    return callable.call();
  }
  public static void main(String[] args) throws IOException {
    f(() -> 1);
    g(() -> 1);
  }
}

interface Callable<V> {
  V call() throws IOException;
}

interface CheckedCallable1<V, E extends IOException> {
  V call() throws E;
}

interface CheckedCallable2<V, E extends IOException> extends Callable<V> {
  @Override V call() throws E;
}

Bu noktada daha çok bir java hatası gibi görünmeye başlıyor ...


17
2018-06-13 06:39


Menşei


Ayrıca, java 8'deki genel istisna türü desteğinin farkında olmanız için +1 - SpaceTrucker
Bu soru nihayet PC'imde Java 8'i çalıştırmam için beni motive etti. KeplerSR2'yi kurdum, jdk1.8.0_05 ... ve bir çalışma zamanı istisnası alıyorum, derleme zamanı değil, aynı noktada: Generics.main (Generics.java:10) Neden: java.lang .ClassFormatError: Sınıf dosyasında yineleme yöntemi adı ve imza Generics $$ Lambda $ 2 Ne verir? - torquestomp
İlginç bir şekilde, Eclipse'in derleyicisi hiçbir hata yayar. Hiçbir neden yok. - awksp
Gerçekten garip hatalar alırken, Eclipse derleyicisine (henüz) güvenmem. Diğer IDE veya javac derleyicisiyle iki kez kontrol etmeyi deneyin. Eclipse'in Java 8 desteği, dürüstlüklerde oldukça sıkıntılı görünüyor. - skiwi
Bu Java hata izleyicisine indi: bugs.java.com/bugdatabase/view_bug.do?bug_id=8047338 - Tobi


Cevaplar:


Bu modeli yasaklayan bir kural olduğunu düşünmüyorum. Derleyici bir hata bulmuşsunuzdur.

Bu modelin, sadece eşdeğer iç sınıf kodunu yazarak, yanlış olmayan kodlara yol açtığını göstermek kolaydır. g(() -> 1);:

g(new CheckedCallable2<Integer, RuntimeException>() {
    public Integer call() {
        return 1;
    }
});

Bu, Java 6 altında bile herhangi bir sorun olmadan derler ve çalışır (Java 5 üzerinde bile çalışacağını varsayardım ama bunu test etmek için hiçbir JDK'm yoktu) ve lambda ile aynı işi yaparken çalışmaması için bir neden yoktur. Netbeans sonuçlarında bu kodu bir lambdaya çevirme tavsiyesinde bile yazar.

Böyle bir yapıyı yasaklayan çalışma zamanı kısıtlaması yoktur. Kaputun altında zorlanan istisna kurallarının olmadığı ve derleme zamanı kontrollerine bağlı olduğu gerçeğinin yanı sıra, derleyicinin, derleyicinin oluşturacağı kodu elle oluşturarak kodumuzu kabul etmesi durumunda da çalışacağını ispatlayabiliriz:

CheckedCallable2<Integer,RuntimeException> c;
try
{
  MethodHandles.Lookup l = MethodHandles.lookup();
  c=(CheckedCallable2)
    LambdaMetafactory.metafactory(l, "call",
      MethodType.methodType(CheckedCallable2.class),
      MethodType.methodType(Object.class),
      l.findStatic(Generics.class, "lambda$1", MethodType.methodType(int.class)),
      MethodType.methodType(Integer.class)).getTarget().invokeExact();
} catch(Throwable t) { throw new AssertionError(t); }
int i=g(c);
System.out.println(i);
// verify that the inheritance is sound:
Callable<Integer> x=c;
try { System.out.println(x.call()); }// throws Exception
catch(Exception ex) { throw new AssertionError(ex); }

…
static int lambda$1() { return 1; }// the synthetic method for ()->1

Bu kod çalışır ve üretir 1 hangisi olursa olsun, beklendiği gibi interface kullanıyoruz call(). Sadece yakalamamız gereken istisnalar farklıdır. Ama söylendiği gibi, bu bir derleme zamanı eseridir.


4
2018-06-13 17:31



Cool, bunu denediğiniz için teşekkürler! Oracle ile bir hata yapacağım. - Tobi