линия, как
A var = new B();
это своего рода сокращение для двух отдельных стадий.
A var; // (1) Make a variable of TYPE A.
var = new B(); // (2) Make an object of CLASS B, that from now on may be
// referred to by the variable var.
Так что переменная имеет ТИП, а объект имеет КЛАСС. Часто они совпадают. Тип переменной часто фактически является классом, хотя и не обязательно. Важно понимать разницу между типом переменной и классом объекта, к которому относится переменная.
Объект обычно относится к нескольким классам. Если класс B расширяет класс A, это означает, что все объекты класса B также являются объектами класса A. И все объекты любого класса вообще являются объектами класса Object
. Другими словами, когда мы говорим, что объект является B, это более специфично, чем говорить, что это A. Как и когда мы говорим, что йог - это медведь, это более специфично, чем говорить, что Йоги - это животное, потому что все неважно являются животные.
Таким образом, переменная типа A действительно может ссылаться на объект класса B, если A - класс, который B расширяет. Но если у вас есть переменная типа A, вы не можете использовать ее для выполнения действий, специфичных для объектов типа B. Например, предположим, что класс A имеет метод, называемый display()
, а класс B имеет метод, называемый explain()
. Компилятор позволит вам вызвать display()
на переменную типа A, но она не позволит вам позвонить explain()
. Если бы это было так, было бы рисковать, если вы вызовете explain()
на объект, который на самом деле не является B, который потерпит неудачу.
Итак, всякий раз, когда существуют методы, которые определяет класс B, для их вызова вам понадобится переменная типа B. Конечно, вы также можете использовать эту же переменную для вызова методов, определенных в классе A. В некотором смысле, если класс B расширяет класс A, то переменная типа B более мощна, чем переменная типа A - вы может делать с ним больше.
Поэтому возникает вопрос - почему я всегда хочу, чтобы написать
A var = new B();
, когда переменная типа B будет более мощным, чем var
в этом примере?
Короткий ответ заключается в том, что он общается с людьми, просматривающими код. В нем говорится: «Да, я знаю, что эта переменная относится к B, но я намереваюсь использовать методы, предоставляемые классом A. Это может быть полезно для кого-то, кто пытается понять ваш код или поддерживать его.
есть также случаи, когда это может сделать реальную разницу вызовов методов, связанных с этой переменной. Предположим, что существует еще один класс C, который имеет два метода с тем же именем, но немного разные подписи, как это.
public class C {
public void process(A arg){
// Do some stuff
}
public void process(B arg){
// Do some other stuff
}
}
В этом конкретный случай, версия process
, которая вызывается, зависит от типа переменной, а не от класса объекта. Поэтому, если вы пишете
C processor = new C();
A var = new B();
processor.process(var);
это будет называть первую версию process
- той, у которой есть подпись. Из-за типа переменной. Но если вы пишете
C processor = new C();
B var = new B();
processor.process(var);
это вызовет вторую версию process
- один с B в подписи.
Когда вы создаете ('новый') A, вы создаете A. Когда вы создаете B, вы создаете B. Но B включает всю функцию A и может использоваться как A в большинстве ситуаций. Если вы помещаете ссылку на B в переменную, напечатанную с помощью «A», вы не можете получить доступ к «B-ness» объекта, только «A-ness». Однако вы можете «вернуть» ссылку на «B», чтобы восстановить доступ к «B-ness»: 'B refB = (B) refA;' Но приведение ('(B)') не будет выполнено во время выполнения если refA фактически не ссылается на B. –