2014-02-10 3 views
5

Этот вопрос относится к моему предыдущему сообщению в here.. Прежде чем я опубликую свой вопрос, я вставляю содержимое из oracle docs;Как стирание ручек переопределяет сценарии в Java?

8.4.8.1. Overriding (by Instance Methods) 

An instance method m1, declared in class C, overrides another instance method m2, declared in class A iff all of the following are true: 

    C is a subclass of A. 

    The signature of m1 is a subsignature (§8.4.2) of the signature of m2. 

8.4.2. Method Signature 
The signature of a method m1 is a subsignature of the signature of a method m2 if either: 

    m2 has the same signature as m1, or 

    the signature of m1 is the same as the erasure (§4.6) of the signature of m2. 

Мое понимание type erasure когда overriding участвует выглядит следующим образом: если после erasure, то signature of m1 and m2 такие же, то тогда они считаются overridden. так в моем предыдущем посте выше я попытался overrideparent class method, который принимает List<String> по subclass method, который принимает List<Integer> предполагая после type erasure , что осталось только List<Object>. но это неправильно. поэтому мое понимание вышеуказанного определения метода overriding, когда вовлечено erasure, является абсолютно неправильным. может кто-нибудь дать простой пример , чтобы объяснить вышеприведенную точку.

Спасибо. btw приведенные выше пункты взяты из here.

ответ

2

Независимо от того, переопределяет ли метод другой, речь идет не только о стираниях методов. Компилятор определяет, переопределяет ли метод другой, и имеет доступ к параметрам типового типа, которые задействованы до того, как произойдет стирание типа.

Ваши мысли об использовании стирания для определения переопределений не совсем правильны. Давайте добавим следующее JLS Section, 8.4.8.1, к обсуждению:

Метод экземпляра m1, объявленный в классе C, перекрывает другой метод экземпляра м2, объявленный в классе A тогда и только тогда все следующие условия:

  • C является подклассом A.

  • Подпись m1 является подзаголовкой (§8.4.2) подписи m2.

  • Либо:

    • м2 являются открытым, защищенным, или объявленным с доступом по умолчанию в том же пакете, C или

    • m1 переопределяет метод м3 (м3 отличающихся от m1, m3 отличная от m2), такая, что m3 переопределяет m2.

Это необходимо, чтобы m1 является подсигнатурой m2, но не наоборот.

Пример 1:

class A { 
    public void foo(List<String> list) { } 
} 

class B extends A { 
    @Override 
    public void foo(List list) {} 
} 

Это является законным, потому что подпись B «foo метода с такой же, как стирание A» foo метода с.

Пример 2:

class A { 
    public void foo(List list) { } 
} 

class B extends A { 
    @Override 
    public void foo(List list) {} 
} 

Это является законным, так как подписи являются одинаковыми (даже если они являются сырые).

Пример 3:

class A { 
    public void foo(List list) { } 
} 

class B extends A { 
    @Override 
    public void foo(List<Integer> list) {} 
} 

Это не законно, так как стирание наиважнейшей метода не принимается во внимание. То есть List<Integer> сравнивается с стиранием List, который по-прежнему остается List, и они не то же самое.

Пример 4:

class A { 
    public void foo(List<String> list) { } 
} 

class B extends A { 
    @Override 
    public void foo(List<Integer> list) {} 
} 

Это опять не законно, так как стирание наиважнейшей метода не принимается во внимание. То есть List<Integer> сравнивается с стиранием List<String> (List), и они не то же самое.

Вы не можете изменить параметры типового типа параметров в методе переопределения (например,List<String> - List<Integer>. Вы не можете ввести дженерики при переопределении метода, который не использовали генерики (например, (List к List<Integer>). Тем не менее, вы можете удалить дженерики при переопределении (например, List<String> к List).

+0

как насчет использования foo (List ) в родительском классе и foo (список ) в подклассе ... это недопустимо, но почему это так? Кстати, ваше объяснение велико. –

+0

Если метод родительского класса имеет «Список ', то он должен иметь возможность принимать' List 'в качестве аргумента. Если дочерний класс имеет «Список », он не может принять «Список » в качестве аргумента. Но чтобы переопределить, он должен принимать те же самые вещи, которые принимает переопределенный метод. Детский метод даже не может иметь параметр «Список », потому что это запретит «Список » в качестве аргумента, который будет принимать суперкласс. Таким образом, подпись переопределяющего метода не может быть 'List ' или 'List '; он должен соответствовать ('List '). – rgettman

+0

Родительский класс может принимать 'Number' или что-нибудь, что' extends Number', такое как 'Double, Integer' и т. Д. Правильно? Детский класс не может 'override' с' List как параметр', и это просто потому, что 'Списки' являются' инвариантными' правильными ?? –

2

Поскольку Java является строго типизированным языком, он должен быть осторожным в отношении ковариации и того, как типы работают вместе. Erasure не является оправданием для нарушения правил типа.

Например:

class BaseGood<T> { 
    public void doStuff(T elem) { 
       // ... 
    } 
} 
class DerivedGood<T> extends BaseGood { 
    public void doStuff(Object elem) { 
     super.doStuff(elem); 
    } 
} 
class BaseBad { 
    public void doStuff(List<Double> list) { 
     // ... 
    } 
} 
class DerivedBad extends BaseBad { 
    public void doStuff(List<Integer> list) { 
     super.doStuff(list); 
     // ... 
    } 
} 

Здесь мы имеем два различных случая для стирания.

С BaseGood и DerivedGood классов, оба метода имеют один и тот же стирание: void doStuff(Object elem), и это может быть известно, что T всегда будет иметь тип Object и, следовательно, функция типобезопасным.

С классами BaseBad и DerivedBad эти два метода имеют одинаковое стирание: void doStuff(List list); однако система типа не может безопасно конвертировать из List<Integer> в List<Double> (или в любой момент от List). Разрешение этого преобразования потенциально может привести к потере Double s в списки Integer s (или проблема, связанная с генериками для обнаружения времени компиляции).

Наличие методов переопределения дружественных после стирания не означает, что система типов не может проверить на наличие несовместимости перед стиранием.

РЕДАКТИРОВАТЬ

Кроме того, в режиме реального стирания применительно к определениям классов/методов не происходит во время компиляции, это происходит во время выполнения. Код, который ссылается на генерические методы, просто компилируется, как если бы функция вызывалась без дженериков, с безопасностью типа, обеспечиваемой компилятором, и проверкой времени выполнения.

См: Java generics - type erasure - when and what happens

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

+0

нормально, я думаю, что я после вашего объяснения. как насчет использования 'double []' и 'int []' в приведенном выше, так как 'массивы'' являются ковариантными', не должно быть проблемы, правильно? –

+0

Это не сработает, потому что 'double' и' int' являются а не в иерархии наследования. 'int' не является супер или подмножеством' double'. Однако, если вы переопределите 'A []' с 'DerivedFromA []', тогда он будет скомпилирован, потому что система типов может гарантировать, что все элементов в 'DerivedFromA []' будет 'A', но если вы попытаетесь сохранить другой класс внутри 'A []', вы получите 'ClassCastException' (в основном, movin g для проверки времени выполнения). – hsun324

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