2013-03-28 1 views
16

Следующий код компилируется в Java 1.6, но не компилируется в Java 1.7. Зачем?Почему этот код компилируется в Java 1.6, но не в Java 1.7?

Соответствующая часть кода является ссылкой на личное поле данных. Ссылка находится внутри того же класса, в котором поле определено, и поэтому кажется законным. Но это происходит с помощью типично типизированной переменной. Этот код - урезанный пример, основанный на классе из собственной библиотеки, - работал в Java 1.6, но не сейчас в Java 1.7.

Я не спрашиваю, как обойти это. Я уже это сделал. Я пытаюсь найти объяснение, почему это больше не работает. Три возможности прийти на ум:

  • Этот код НЕ ЮРИДИЧЕСКАЯ в соответствии с JLS и никогда не должны быть скомпилированы (была ошибка в 1.6 компилятором, зафиксированной в 1.7)
  • Этот код ПРАВОВАЯ в соответствии с JLS и должна составить (а обратная совместимость ошибка была введена в 1.7 компилятор)
  • Этот код попадает в серой области в 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; 
    } 

} 

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 

Добавление. Такая же проблема возникает, если ссылка относится к частному методу вместо частной переменной-члена. Это работает в Java 1.6, но не в 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; 
    } 

} 

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

Я бы предложил декомпилировать оба сгенерированных файла класса, тогда разница должна быть очевидной. – Landei

+0

@ Landei В случае с 1.7 нет файла сгенерированного класса, поскольку компилятор отказывается его скомпилировать. –

ответ

18

Продемонстрированная проблема, похоже, соответствует поведению сообщается в Oracle bug 6904536. Ошибка была закрыта как «Не проблема» со следующим объяснением:

javac ведет себя в соответствии с JLS. См. Также 6558551, 6711619 и связанной с этим проблеме JLS 6644562.

Соответствующий JLS вопрос не решен, со следующим комментарием:

Упрощенное объяснение членства переменных типа является приветствуется. Существует общая трудность с частными членами типа переменных .Формально такие члены не становятся членами самого переменной типа, хотя Javac и Eclipse, традиционно сделали их членов и код полагаться на что:

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 
    } 
} 

Петр представил подобный тест:

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 
    } 

    } 
} 

Поскольку типы пересечений построены по наследству, а частные члены никогда не унаследованы, сложно переопределить типы пересечений , чтобы иметь частных членов. Тем не менее, это была бы совместимая вещь .

Для справки, соответствующий раздел JLS - §4.4.

EDIT:

Я склонен согласиться с JLS здесь на самом деле, потому что он совпадает с самим собой, когда мы убираем дженерики из картины. Рассмотрим следующий пример:

static class Parent { 

    private int i; 

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

static class Child extends Parent { } 

child.i не видно, потому что доступ к закрытым членам не передается по наследству. Эта точка пригнаны тем, что Child может иметь свой собственный i без затенения:

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

Так что это будет редкий пример приведение к базовому типу является необходимым:

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

Так с наследуется доступность на картинке, JLS выглядит здесь правильно, учитывая, что V в Foo<V extends Foo<V>> не обязательно Foo<V>, но может быть какой-то тип, который расширяет Foo<V>.

+0

Спасибо, Пол. Да, ответ, похоже, заключается в том, что javac в 6 скомпилировал определенные формы, которые не были в строгом соответствии с JLS, причем приведенный выше пример. javac в 7 исправил это. В частности, доступ к частным полям/методам через общую ссылку * даже в пределах одного класса * никогда не должен быть разрешен. (Какая жалость. Я не эксперт по JLS, но я не могу сразу понять, почему этого не должно быть.) Короче говоря, у компилятора JDK 1.6 была ошибка. – Paul

+1

@Paul Нет проблем, спасибо за интересный пост. Посмотрите мое редактирование, если вам интересно. –

+4

Очень хороший ответ! Спасибо за проведение исследований и объяснение. – Jesse

 Смежные вопросы

  • Нет связанных вопросов^_^