Java Narrowing Reference Преобразование из типа в интерфейс - PullRequest
0 голосов
/ 15 января 2019

Пытается понять сужение Java-преобразования из класса в интерфейс. Состояние JLS ( JLS-5.1.6 ):

Из любого класса класса C в любой непараметрический интерфейс типа K, при условии, что C не является окончательным и не реализует K.

Чтобы проверить это, я создал класс и интерфейс. Затем попытался привести класс к интерфейсу, но получил исключение ClassCastException. Это образец моего кода.

class NarrowingReferenceConversion
{
    public static void main(String args[])
    {

        S s = new S();
        T t = (T)s;

    }
}

interface T
{
    public void print();
}

class S
{
    public void print(){
        System.out.println("S.print()");
    }
}

При компиляции и запуске выше, я получаю следующее сообщение об ошибке:

Исключение в потоке "main" java.lang.ClassCastException: S не может быть приведено к T

Ответы [ 2 ]

0 голосов
/ 15 января 2019

Простая вещь:

 S s = new S();
 T t = (T)s;

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

Но дело в том: ваш случай здесь является довольно конкретным примером. Обычный вариант использования менее «понятен». Как показал Эран, довольно просто построить подобный пример, где приведение во время выполнения может работать или нет, в зависимости от очень тонких различий.

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

Другими словами: когда вы разрабатываете языки и конструируете компиляторы, всегда есть компромиссы. Как в: иногда просто не стоит добавлять очень специфическую проверку во время компиляции. Вы скорее соглашаетесь с тем, что более общее правило может привести к сбоям во время выполнения, а не во время компиляции.

0 голосов
/ 15 января 2019

Это преобразование, которое не гарантированно работает, точно так же, как преобразование ссылки базового класса в подкласс не гарантируется. Вот почему это считается сужением конверсии.

Компилятор знает, что преобразование может работать во время выполнения, поэтому он допускает это, но если оно не работает, ClassCastException выбрасывается во время выполнения.

Только если вы назначите s экземпляр подкласса S, который реализует интерфейс T, преобразование будет работать.

class NarrowingReferenceConversion
{
    public static void main(String args[])
    {

        S s = new S2();
        T t = (T) s; // this will work, since S2 implements T

    }
}

interface T
{
    public void print();
}

class S
{
    public void print(){
        System.out.println("S.print()");
    }
}

class S2 extends S implements T
{
}

Давайте объясним два условия этого преобразования:

  1. «C не является окончательным» - если бы оно было final, не было бы подклассов C, поэтому компилятор точно знает, что это преобразование никогда не будет работать, и компиляция завершится неудачей.

  2. "не реализует K" - если C реализует K, это больше не сужающее преобразование. Он становится расширяющимся эталонным преобразованием , гарантированно преуспевающим во время выполнения. На самом деле, не было бы необходимости использовать оператор приведения. Подойдет простое задание.

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