Порядок приведения Java

Допустим, у меня есть следующая настройка

class A {
    B foo();
}

class C extends B {

}

// later
A a = new A();
C theFoo = (C)a.foo();

Мы знаем, что a.foo() возвращает тип B.

Когда я делаю (C)a.foo(), это

  1. Приведение a к типу C, а затем попытка вызова foo() на нем?
  2. Вызов foo() для a и приведение результата к типу C?

Мне трудно определить, и я всегда просто играл на стороне осторожности с дополнительными скобками (что неплохая идея для удобочитаемости, но теперь мне любопытно)

Это конкретно относится к ObjectInputStream.readObject(), хотя я не понимаю, как это изменит поведение.


person corsiKa    schedule 23.04.2011    source источник
comment
Быстрый способ выяснить это — установить точки останова в C.foo и B.foo и выполнить отладку. Тогда тот, кто будет поражен, ответит на ваш вопрос. кстати отличный вопрос   -  person Spidy    schedule 23.04.2011
comment
Хм, интересно, позволит ли Eclipse установить точки останова в основных классах Java... :-)   -  person corsiKa    schedule 23.04.2011
comment
Вы можете сделать это в своих собственных библиотеках, на которые ссылаются, поэтому я думаю, что основные библиотеки реагируют одинаково. Пока у вас есть исходный код, он позволяет вам устанавливать точки останова (не проверял, действительно ли они срабатывают)   -  person Spidy    schedule 23.04.2011


Ответы (2)


(C)a.foo() эквивалентно (C)(a.foo()), то есть #2 в вопросе.

Чтобы получить #1, вам нужно будет написать ((C)a).foo().

Спецификация языка Java не определяет приоритет операций в удобной для чтения сводке.

Приложение A книги Введение в программирование на Java Седжвика и Уэйна исчерпывающая таблица приоритета операторов.

Приложение B Язык программирования Java содержит таблицу приоритет оператора, но он не такой полный, как у Седжвика.

Внимательное изучение грамматики в Спецификации языка Java. может определить относительный приоритет рассматриваемых выражений приведения и вызова метода:

Expression:
        Expression1 [AssignmentOperator Expression1]]

Expression1:
        Expression2 [Expression1Rest]

Expression1Rest:
        ?   Expression   :   Expression1

Expression2 :
        Expression3 [Expression2Rest]

Expression2Rest:
        {InfixOp Expression3}
        Expression3 instanceof Type

Expression3:
        PrefixOp Expression3
        (   Expression | Type   )   Expression3
        Primary {Selector} {PostfixOp}

Primary:
        ParExpression
        NonWildcardTypeArguments (ExplicitGenericInvocationSuffix | this Arguments)
        this [Arguments]
        super SuperSuffix
        Literal
        new Creator
        Identifier { . Identifier }[ IdentifierSuffix]
        BasicType {[]} .class
        void.class

Соответствующие производства выделены жирным шрифтом. Мы видим, что выражение приведения соответствует произведению Expression3 : (Expression|Type) Expression3. Вызов метода соответствует продукции Expression3 : Primary {Selector} {PostfixOp} посредством продукции Primary: Identifier {. Identifier }[IdentifierSuffix]. Собрав это вместе, мы видим, что выражение вызова метода будет рассматриваться как единица (Expression3), над которой будет действовать приведение.

Хм, диаграмме приоритета легче следовать... ;)

person WReach    schedule 23.04.2011
comment
Кажется, это согласуется с сообщениями, которые я получаю от Eclipse. Знаете ли вы что-нибудь из JLS, что может поддерживать это? Я не смог его найти (что меня удивило. Должно быть, я просто использовал неправильные ключевые слова в своем Google-фу). - person corsiKa; 23.04.2011
comment
@glowcoder: рассмотрите возможность изучения грамматики, указанной в Спецификация языка Java, в частности определение Expression3. - person Chris Jester-Young; 23.04.2011
comment
К сожалению, я не знаю ни одного содержательного описания приоритета операторов в официальной публикации Java. Как говорит @Chris, вам придется разобраться с грамматикой. Такой анализ проводили и другие, например, Sedgewick and Wayne. - person WReach; 23.04.2011
comment
Ух ты. Спасибо за редактирование, это определенно проясняет ситуацию (по крайней мере, для тех из нас, кто может возиться с грамматикой). Вы знаете, для группы людей, которые так увлечены тем, чтобы все в языке было таким же простым для понимания насколько это возможно, и сделать вещи удобными в сопровождении, удивительно видеть такой плохой выбор имен элементов грамматики. Представьте, что вы работаете с кодом, в котором ваши переменные были названы таким образом. (Хорошо, нам не нужно воображать, мы делали это раньше, и мы ворчим об этом, когда делаем!) - person corsiKa; 23.04.2011

Вызов метода имеет более высокий приоритет оператора, чем приведение типа, поэтому (C) a.foo() сначала вызовет a.foo() и привести результат к типу C. Напротив, ((C) a).foo() сначала приводит a к типу C, а затем вызывает свой метод foo().

person Josh Rosen    schedule 23.04.2011
comment
Эта ссылка, кажется, не работает. - person Jouni K. Seppänen; 10.10.2014