2013-03-28 16 views
16

Następujący kod kompiluje dobrze w Javie 1.6, ale nie kompiluje się w Javie 1.7. Czemu?Dlaczego ten kod kompiluje się w Javie 1.6, ale nie w Javie 1.7?

Odpowiednią częścią kodu jest odniesienie do prywatnego pola danych. Odniesienie pochodzi z tej samej klasy, w której pole jest zdefiniowane, a więc wydaje się legalne. Ale dzieje się to za pośrednictwem zmiennej generowanej maszynowo. Ten kod - uproszczony przykład oparty na klasie z wewnętrznej biblioteki - działał w Javie 1.6, ale nie w Javie 1.7.

Nie pytam, jak to obejść. Już to zrobiłem. Próbuję znaleźć wyjaśnienie, dlaczego to już nie działa. Trzy możliwości przyjść do głowy:

  • Ten kod jest NIE PRAWNA według JLS i nigdy nie powinny być zestawiane (był to błąd w 1,6 kompilatora, utrwalano w 1,7)
  • Ten kod jest PRAWNA według JLS i należy opracować (wsteczną kompatybilność błąd został wprowadzony do 1,7 kompilator)
  • Kod ten spada do szary obszar w JLS

Foo.java:

import java.util.TreeMap; 
import java.util.Map; 

public abstract class Foo<V extends Foo<V>> { 

    private final Map<String,Object> data = new TreeMap<String,Object>(); 

    protected Foo() { ; } 

    // Subclasses should implement this as 'return this;' 
    public abstract V getThis(); 

    // Subclasses should implement this as 'return new SubclassOfFoo();' 
    public abstract V getEmpty(); 

    // ... more methods here ... 

    public V copy() { 
     V x = getEmpty(); 
     x.data.clear();  // Won't compile in Java 1.7 
     x.data.putAll(data); // " 
     return x; 
    } 

} 

wyjście Compiler:

> c:\tools\jdk1.6.0_11\bin\javac -version 
javac 1.6.0_11 

> c:\tools\jdk1.6.0_11\bin\javac c:\temp\Foo.java 

> c:\tools\jdk1.7.0_10\bin\javac -version 
javac 1.7.0_10 

> c:\tools\jdk1.7.0_10\bin\javac c:\temp\Foo.java 
Foo.java:18: error: data has private access in Foo 
     x.data.clear(); 
     ^
Foo.java:19: error: data has private access in Foo 
     x.data.putAll(data); 
     ^
2 errors 

Uzupełnienie. Ten sam problem występuje, jeśli odwołanie odnosi się do metody prywatnej zamiast do prywatnej zmiennej członkowskiej. Działa to w Javie 1.6, ale nie w 1.7.

Foo2.java:

import java.util.TreeMap; 
import java.util.Map; 

public abstract class Foo2<V extends Foo2<V>> { 

    private final Map<String,Object> data = new TreeMap<String,Object>(); 

    protected Foo2() { ; } 

    // Subclasses should implement this as 'return this;' 
    public abstract V getThis(); 

    // Subclasses should implement this as 'return new SubclassOfFoo();' 
    public abstract V getEmpty(); 

    // ... more methods here ... 

    public V copy() { 
     V x = getEmpty(); 
     x.theData().clear();  // Won't compile in Java 1.7 
     x.theData().putAll(data); // " 
     return x; 
    } 

    private Map<String,Object> theData() { 
     return data; 
    } 

} 

wyjście Compiler:

> c:\tools\jdk1.6.0_11\bin\javac c:\temp\Foo2.java 

> c:\tools\jdk1.7.0_10\bin\javac c:\temp\Foo2.java 
Foo2.java:18: error: theData() has private access in Foo2 
     x.theData().clear(); 
     ^
Foo2.java:19: error: theData() has private access in Foo2 
     x.theData().putAll(data); 
     ^
+0

Sugerowałbym dekompilację obu wygenerowanych plików klas, wtedy różnica powinna być oczywista. – Landei

+0

@Landei Nie ma wygenerowanego pliku klasy w przypadku 1.7, ponieważ kompilator odmawia skompilowania go. –

Odpowiedz

18

Wykazana problem wydaje się dopasować zachowanie zgłoszone w Oracle bug 6904536. Błąd został zamknięty jako "Not An Issue" z następującym wyjaśnieniem:

javac zachowuje się zgodnie z JLS. Zobacz także 6558551, 6711619 i powiązany problem JLS 6644562.

Odpowiedni JLS problem jest nierozwiązany, z następującym komentarzem:

Uproszczony wyjaśnienie przynależności zmiennych typu jest mile widziane. Występuje ogólna trudność z prywatnymi członkami zmiennych typu .Formalnie takie elementy nie stają się członkami samej zmiennej typu, choć javac i Eclipse tradycyjnie im członków i kod doszła polegać na tym:

class Test { 
    private int count = 0; 
    <Z extends Test> void m(Z z) { 
    count = z.count; // Legal in javac 1.6, illegal in javac 1.7 due to fix for 6711619 
    } 
} 

Peter złożyła podobny test:

class A { 
    static class B { private String f; } 

    abstract static class Builder<T extends B> { 
    abstract T getB(); 

    { 
     ((B)getB()).f.hashCode(); 
     getB().f.hashCode(); // error: f has private access in A.B 
    } 

    } 
} 

Ponieważ typy skrzyżowań są tworzone przez dziedziczenie, a prywatne elementy nigdy nie są dziedziczone, trudno jest ponownie określić typy przecięcia , aby mieć członków prywatnych. Niemniej jednak byłaby to kompatybilna rzecz do zrobienia.

Dla odniesienia, odpowiednią sekcją JLS jest §4.4.

EDIT:

I mają tendencję do uzgodnienia z JLS tu rzeczywiście, ponieważ dopasowuje się sama ze sobą, gdy usuwamy rodzajowych z obrazka. Rozważmy następujący przykład:

static class Parent { 

    private int i; 

    void m(Child child) { 
     i = child.i; //compile error 
    } 
} 

static class Child extends Parent { } 

child.i nie jest widoczna, ponieważ dostęp do członków prywatnych nie jest dziedziczona. Ten punkt jest napędzany domu przez fakt, że Child może mieć własne i bez cienia:

static class Child extends Parent { 
    private int i; //totally fine 
} 

Więc byłoby to rzadki przykład upcasting konieczności:

void m(Child child) { 
    i = ((Parent)child).i; 
} 

Więc z dziedziczone dostępność poza obrazem, JLS wydaje się tutaj poprawny, biorąc pod uwagę, że V w Foo<V extends Foo<V>> niekoniecznie jest Foo<V>, ale może być pewnego rodzaju, który rozszerza się o Foo<V>.

+0

Dzięki Paul. Tak, odpowiedź wydaje się, że javac w 6 skompilował pewne formy, które nie były w ścisłej zgodności z JLS, powyższy jest przykładem. javac w 7 naprawił to. W szczególności dostęp do prywatnych pól/metod przez ogólne odwołanie * nawet w obrębie tej samej klasy * nigdy nie powinien był być dozwolony. (Szkoda, nie jestem ekspertem od JLS, ale nie mogę od razu zobaczyć, dlaczego tak nie powinno być.) W skrócie, kompilator JDK 1.6 miał błąd. – Paul

+1

@Paul Bez problemu, dzięki za interesujący wpis. Zobacz moją edycję, jeśli jesteś zainteresowany. –

+4

Bardzo ładna odpowiedź! Dziękuję za wykonanie badań i wyjaśnienie. – Jesse

Powiązane problemy