Какой класс получает целевой объект после приведения? - PullRequest
1 голос
/ 01 сентября 2010

ОК, вопрос нуб.Я учусь в SCJP и получил 3 вопроса о неправильном приведении ссылок на объекты, которые, похоже, указывают на одно и то же недоразумение.Просто хотел подтвердить, каким должно быть правильное понимание.Хорошо, вот вопросы:

1. class CodeWalkFour {
2.    public static void main(String[] args){
3.       Car c = new Lexus();
4.       System.out.print(c.speedUp(30) + " ");
5.       Lexus l = new Lexus();
6.       System.out.print(l.speedUp(30, 40, 50));
7.     }
8.  }
9.  class Car {
10.     private int i=0;
11.    int speedUp(int x){
12.        return i;
13.    }
14. }
15. class Lexus extends Car {
16.     private int j = 1;
17.     private int k = 2;
18.       int speedUp(int y){
19.       return j;
20.     }
21.     int speedUp(int... z){
22.         return k;
23.      }
24.  }

Я думал, что после строки 3 c будет Car, а не Lexus, поэтому будет вызван метод Car.speedUp, а не метод Lexus.speedUp.Оказывается, это последнее, что называется.

2.
1. class StudentProb {
2.   private int studentId  =  0;
3.   void setStudentID(int sid) {
4.      student_id = sid;
5.      System.out.println("Student ID has been set to " + sid);
6.   }
7.   public static void main(String args[]) {
8.       int i = 420;
9.       Object ob1;
10.      StudentProb st1 = new StudentProb();
11.       ob1 = st1;
12.       st1.setStudentID(i);
13.   }
14. }

Та же проблема.Я думал, что строка 11 сделает st1 объектом, а не StudentProb.Как компилятор все еще знает, где найти setStudentID?

3.
1.    LectureHall lh = new LectureHall();
2.   Auditorium a1;
3.   Facilities f1;
4.
5.    f1 = lh;
6.    a1 = f1;

Услуги - это интерфейс.Класс ClassRoom реализует объекты, а Auditorium и LectureHall являются подклассами ClassRoom.Тот же вопрос: я думал, что после 5-й строки f1 и lh будут LectureHall.Но f1 - все еще Услуги.Так что же здесь делает кастинг?

Спасибо всем!

PS: форматирование кода у меня как-то не работает.Не стесняйтесь редактировать.

Ответы [ 5 ]

2 голосов
/ 01 сентября 2010

Объект всегда является экземпляром определенного класса, вы можете ссылаться на экземпляр, используя любой из суперклассов, но экземпляр не меняется. Я думаю, что второй отрывок иллюстрирует это лучше всего, вы не можете написать ob1.setStudentID(i);, потому что ob1 - это переменная Object, хотя фактический класс равен StudentProb

В вашем 3-м фрагменте 5-я строка недопустима, поскольку Facilities - это суперкласс Auditorium, так что вы можете назначить экземпляр Auditorium для переменной Facilities, но не наоборот.

2 голосов
/ 01 сентября 2010

Во время выполнения каждый объект знает, каков его собственный класс, то есть класс, которым он был фактически создан.Его можно назначить переменной этого класса или любому суперклассу.При выполнении функции вы получаете «версию» этой функции для класса, в котором был создан объект, а НЕ для класса, в котором переменная, содержащая ссылку на объект, была объявлена ​​как.пример вашего автомобиля / лексуса.Если вы напишите «Lexus mycar = new Lexus (); mycar.speedUp ();», то будет выполняться Lexus.speedUp, а не Car.speedUp.Может быть, это очевидно.Но даже если вы напишите «Автомобиль mycar = новый Lexus (); mycar.speedUp ();»выполняется все еще Lexus.speedUp, потому что это класс реального объекта.Вы можете переназначить объект различным переменным разных классов, как вам угодно, объект все еще знает свой «реальный» класс.

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

Во время COMPILE компилятор не знает класс какого-либо данного объекта.Например, если вы напишите:

void speed1(Car somecar)
{
  somecar.speedUp(1);
}

Компилятор не знает, является ли автомобиль здесь Lexus или Honda, или что.Он просто знает, что это машина, потому что не знает, где и как будет вызываться эта функция.Фактический тип машины не будет известен до времени выполнения.

Следствием этого является то, что если вы попытаетесь написать:

void speed1(Object somecar)
{
    somecar.speedUp(1);
}

Компилятор выдаст ошибку на этом,У него нет возможности узнать, что Object - это Автомобиль, поэтому он не знает, что speedUp - допустимая функция.

Даже если вы написали:

Object mycar=new Lexus();
mycar.speedUp(1);

Вы получите ошибку.Как человек, читающий код, вы можете легко увидеть, что mycar должен быть Lexus и, следовательно, Car, но компилятор просто видит, что mycar объявлен как Object, а Object не имеет функции speedUp.(Полагаю, достаточно умный компилятор мог бы понять в этом тривиальном примере, что mycar должен быть Lexus, но компилятор не может работать с тем, что он может знать иногда или большую часть времени, он должен иметь дело с абсолютами.)

Редактировать: Ответ на вопрос в комментарии.

RE вопрос 3: Я понимаю, где вы здесь запутались.Вы должны отличать СКОРОСТЬ ВРЕМЕНИ от РАБОТЫ.

Когда вы выполняете функцию над объектом, вы получаете «версию» этой функции, специфичную для «реального» класса этого объекта.Например, если вы напишите:

Car car1=new Lexus();
Car car2=new Chrysler(); // assuming you defined this, of course
car1.speedUp(1);  // executes Lexus.speedUp
car2.speedUp(2);  // executes Chrysler.speedUp

Но это БЕЗОПАСНО.Во время COMPILE все, что знает компилятор, это тип переменной, содержащей ссылку.Это может быть то же самое, что и «реальный» класс объекта, или это может быть любой суперкласс вплоть до Object.В обоих случаях это Автомобиль.Так как Car определяет функцию speedUp, вызов car1.speedUp является законным.Но вот что важно: во время компиляции Java знает, что у Car есть функция speedUp, поэтому вызов speedUp для объекта Car является законным.Но он не знает и не заботится о том, какую функцию ускорения вы получите.То есть, когда вы говорите car2.speedUp, Java знает, что car2 - это Car, потому что это объявленный тип.Он не знает - помните, что мы говорим во время компиляции, а не во время выполнения - он не знает, является ли это Lexus или Chyrsler, просто что это автомобиль.Только во время выполнения он узнает, какой это тип машины.

1 голос
/ 01 сентября 2010

Это помогает мне - это может не помочь вам, но я брошу это туда.

Визуализируйте наложение объекта, как надевание нового предмета на этот объект.Он имеет другой внешний вид, но под любой одеждой, которую вы надеваете, это все тот же объект.

Например, если вы берете String и приводите его к Object, он не становится объектом - но этоносит одежду объекта.Вы не можете вызвать .length для объекта, потому что этот метод не существует в одежде "Object", но если вы вызовете .equals или .hashCode, вы получите те же точные ответы, что и при вызове их в строке, потому чтоони обращаются через одежду к нижележащему объекту.

Одежда просто скрывает методы, недоступные классу.Объект o = новая строка ();скрывает все методы, которые есть в строке, но не объект под новой одеждой, чтобы он выглядел как объект.

Вы можете позже переодеть ваш объект как строку.

Одежда, которую выизнос не имеет отношения к работе объекта, а только к тому, как внешний мир взаимодействует с объектом / просматривает его.

Возможно, это немного отодвигает аналогию, но также следует учесть, что в вашей «одежде» есть методы, которыене существует в вашем объекте, нет смысла пытаться носить эту одежду, поэтому вы не можете сделать это:

String s=new Object();

Поскольку объект не может заполнить иск String, String'sВ костюме есть отверстия для «длины» и «добавления», а также многих других методов, которые Объект просто не может заполнить - например, без рук, пытающихся надеть перчатки.

1 голос
/ 01 сентября 2010

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

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

0 голосов
/ 12 февраля 2017

Помните еще одну вещь. Ссылка на подкласс не может содержать объект суперкласса. class A{} class B extends A{} Здесь вы можете создать A a=new A(); A a=new B();B b=new B() Но вы не можете создать B b=new A()

...