Приведение ссылочной переменной в Java - PullRequest
3 голосов
/ 31 января 2009

У меня есть что-то неясное относительно приведения ссылочной переменной в Java.

У меня есть два класса A и B. A - суперкласс B. Если у меня есть два объекта, а затем оператор print:

A a = new A(); //superclass

B b = new B(); //subclass

System.out.println ((A)b);

тогда что именно происходит при выполнении метода println?

Я знаю, что, поскольку B является подклассом A, мне разрешено делать следующее приведение:

A a2 = (A)b;

Я также знаю, что когда println принимает в качестве аргумента ссылочную переменную, то вызывается метод toString () класса, который создал аргумент объекта (неявно). Это так, потому что метод println () ищет аргумент типа String, а метод toString () представляет объект в виде строки. И даже если мы не пишем toString (), метод вызывается - неявно. Итак, следующие два утверждения эквивалентны:

System.out.println (b);

System.out.println (b.toString());

Итак, мой вопрос: каково неявное действие, предпринимаемое, когда у нас есть

System.out.println ((A)b); 

?

Я полагаю, что тип ссылочной переменной b автоматически изменяется с B на A. Переменная должна по-прежнему указывать на тот же объект - тот, который создан с помощью

B b = new B();

но теперь изменился бы только тип b. Это правильно? Другой вопрос: даже если я изменил тип b на тип суперкласса, будут ли вызываться переопределенные методы в подклассе, а не методы суперкласса?

Большое спасибо.

Привет

Ответы [ 8 ]

4 голосов
/ 31 января 2009

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

System.out.println (XXX) принимает параметры разных типов (несколько перегруженных версий), но в этом случае вы получите версию, которая принимает Object. Так как каждый объект в Java поддерживает toString (), toString вызывается для фактического аргумента, независимо от того, что это такое.

Теперь, поскольку все методы в Java отправляются динамически, версия, которая запускается, является версией, соответствующей динамическому типу. Преобразование объекта из B в A изменяет только статический (объявленный) тип выражения. Динамический тип (что там на самом деле) все еще является B. Следовательно, версия в B вызывается.

3 голосов
/ 31 января 2009

Существует множество объявлений println(...) в классе PrintStream (тип System.out).

Два из них:

void println(String x)
void println(Object x)

Когда вы вызываете println((A)b), компилятор выбирает println(Object), потому что A не String (или любой другой тип, поддерживаемый println). Когда вы вызываете println(b.toString()), компилятор выбирает println(String), потому что вы передаете строку.

В вашем случае приведение b к A не имеет никакого эффекта, так как println () не имеет объявления ни для типов A, ни для B. Но приведение все равно произойдет (потому что вы просили об этом), или, может быть, этого не произойдет, потому что компилятор оптимизирует его, так как знает, что оно избыточно и не может дать сбой и не имеет никакого эффекта.

Не идиоматично писать:

A a2 = (A)b;

, поскольку это избыточно, поскольку B является подклассом A. Возможно, компилятор оптимизирует преобразование (что является операцией во время выполнения для проверки , принадлежит ли объект определенному типу , никогда не меняй его тип).

После создания объекта типа B его тип никогда не изменяется . Это всегда B:

class B extends/implements A {...}
B b = new B();   // construct a B
A a = b;         // assign a B to an A variable, it's superclass
A a = (A) b      // as above including check to see that b is an A (redundant, may be optimised away).

B b = a;         // Syntax error, won't compile
B b = (B) a      // Will check whether a is of type B then assign to variable b

В последнем случае, поскольку B является подклассом A, может случиться так, что a содержит экземпляр B и приведение будет успешным. Или может случиться так, что a содержит экземпляр некоторого другого класса, который расширяет / реализует / равен A и не является B, и вы получите ClassCastException.

Таким образом, поскольку объект типа B всегда сохраняет свою идентичность (это "B" -ness), то любые (instance-) методы, вызываемые для этого объекта, всегда будут вызывать реализацию B независимо от того, была ли переменная, через которую вы обращаетесь к объекту, объявлен как A или B.

Помните, что вы можете вызывать только те методы, которые объявлены в классе, для которого определена переменная.

Так, например, если B объявляет метод b_only(), то компилятор не позволит вам написать a.b_only(); Вы могли бы написать ((B)a).b_only() хотя.

1 голос
/ 17 декабря 2010

Я думаю, что когда мы используем ссылочную переменную в Java и с помощью этой переменной мы можем назначить объект любого типа класса. В большинстве случаев мы создаем ссылочную переменную интерфейса и абстрактного класса, потому что мы не можем создать объект интерфейса и абстрактного класса, поэтому присваиваем объект класса в ссылочной переменной интерфейса или абстрактного класса. Экс-

Interface X {
 public abstract void xx();
 public abstract void yy();
}

Class XXX implements X {
   ...........
}

Class XY extends XXX {
  X xy = new XXX();
} 

здесь xy - ссылка на интерфейс X и назначение объекта класса XXX в ссылке на интерфейс.

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

1 голос
/ 31 января 2009

Всегда думайте о своем объекте как о типе, для которого он создан (B в вашем случае). Если он настроен на А, думайте об этом как о, хм, думайте, что Б надевает одежду. Он может выглядеть как А, и вы не сможете выполнять какие-либо приятные вещи В, которые хотите делать, но внутри одежды это все равно В - одежда совсем не меняет нижележащий объект.

Таким образом, сводка будет такова - вы можете вызывать только методы в A, но когда вы вызываете его, он проходит прямо и выполняет его, как если бы это был B.

1 голос
/ 31 января 2009

Поскольку все методы Java имеют динамическую диспетчеризацию, функция, которая вызывается, не зависит от статического типа ссылки. Следовательно, результаты будут одинаковыми как с актерами, так и без них. [Результаты могут отличаться, если вы понижаете - версия для кастинга может вызвать исключение]

1 голос
/ 31 января 2009

Это правильно?

Вроде. Результат выражения приведения будет иметь тип А. Тип переменной 'b' всегда будет иметь тип B.

Другой вопрос: хотя я изменил тип b на тип суперкласса, будут ли вызываться переопределенные методы в подклассе, а не методы суперкласса?

Будут вызваны методы экземпляра базового объекта . Пример:

class Foo {
    public static void main(String[] args) {
        B b = new B();
        assert "B".equals(((A) b).m());
    }
}

class A {
    String m() { return "A"; }
}

class B extends A {
    String m() { return "B"; }
}
0 голосов
/ 19 августа 2013

вопрос: хотя я изменил тип b на тип суперкласса, будут ли вызываться переопределенные методы в подклассе, а не методы суперкласса?

в этом случае вызывается метод подкласса b ; убедительно понять почему; Вы можете относиться к следующему сценарию реального мира

Рассмотрим родительский класс Отец , демонстрирующий поведение (метод): Рост определяется как

отец высокий; рост = 6'2 "

Сын - это дочерний класс, унаследовавший от отца поведение рост , в результате он также высокий; высота 6 ', явно преобладающая над поведением

всякий раз, когда ваш подкласс Сын называет высоту поведения своим именем, он отображает переопределенное поведение , то есть его собственный рост 6 '.

0 голосов
/ 02 февраля 2009

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

Обратите внимание, что это НЕ относится ко всем методам, поскольку только переопределенные методы динамически связаны. Перегруженные методы статически связаны. Во многих ответах здесь упоминается, что java-методы всегда динамически связаны, что неверно.

См. Этот вопрос для более подробного объяснения.

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