2013-10-05 3 views
4

Ok .. Так, Если у вас есть иерархия классов, таких какОбъект

public class A {...} 

и,

public class B extends A {...} 

... При создании объектов, в чем разница между :

A object = new A(); 
A object = new B(); 
B object = new B(); 

Благодарим за ваше время.

+0

Когда вы создаете ('новый') A, вы создаете A. Когда вы создаете B, вы создаете B. Но B включает всю функцию A и может использоваться как A в большинстве ситуаций. Если вы помещаете ссылку на B в переменную, напечатанную с помощью «A», вы не можете получить доступ к «B-ness» объекта, только «A-ness». Однако вы можете «вернуть» ссылку на «B», чтобы восстановить доступ к «B-ness»: 'B refB = (B) refA;' Но приведение ('(B)') не будет выполнено во время выполнения если refA фактически не ссылается на B. –

ответ

1
A object = new B(); 

Это заявляет, что object будет ссылаться на объект класса A или любого из его подклассов (если это не null). Компилятор будет рассматривать его как объект типа A, поэтому вы можете получить доступ только к методам и полям, которые объявлены для A (или одного из его суперклассов). Это также означает, что вы можете позже присвоить его какой-либо другой объекта, класса A или подкласса:

A object1 = new B(); 
B object2 = new B(); 

// reassign later 
object1 = new A(); // legal 
object2 = new A(); // ILLEGAL 

class C extends A { ... } 
object1 = new C(); // legal 
object2 = new C(); // ILLEGAL 

Таким образом, первоначальная декларация объявляет object как имеющий тип A. Но его начальное значение является объектом типа B, что нормально, потому что B является подклассом A.

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

2
public class A 
{ 
    public void methodA(){} 
} 
public class B extends A 
{ 
    public void methodB(){} 
} 

Надеюсь, это может продемонстрировать разницу.

A obj = new A(); 

a.methodA(); //works 

A obj = new B(); 

obj.methodA(); //works 
obj.methodB(); //doesn't work 
((B)obj).methodB(); //works 

B obj = new B(); 

obj.methodA(); //works 
obj.methodB(); //works 
1
A object = new A(); 

object типа A (вы можете получить доступ к поля или метода из A)

A object = new B(); 

object типа A (вы не можете получить доступ поля или метода из B, только из A)

B object = new B(); 

object типа B (вы можете получить доступ поля или метод из A и B)

1
A object1 = new A(); 
A object2 = new B(); 
B object3 = new B(); 

object1 объявляется в качестве ссылки на объект. Так как класс B расширяет класс A, его можно установить либо либо (new A(), либо new B() будет действительным).

object2 объявлен как ссылка на объект A, но на самом деле является объектом B. Скажем, класс B имеет метод, называемый eatFood(). Если вы попытались получить доступ к этому методу с помощью object2.eatFood(), компилятор выдаст ошибку, потому что метод eatFood находится только в классе B.Несмотря на то, что объект фактически является объектом B, компилятор считает, что это объект A из-за объявления типа. Чтобы получить доступ к методу eatFood, вам нужно было бы его вывести: ((B)object2).eatFood().

object3 - это просто ссылка на объект B, а на самом деле есть объект B. Он может получить доступ к методам A, а также к методам B.

+1

Я считаю, что это должно быть '((B) object2) .eatFood()' из-за приоритета оператора трансляции ... Я все время сталкиваюсь с этой проблемой. – ajb

2
A object = new A(); 

Вы создаете в ссылке типа A. Вы можете можете получить доступ только к методам/свойствам и родителей методы/свойства в A instance.

A object = new B(); 

Вы создаете B instance в ссылке типа А. Таким образом object может вести себя полиморфного образом, например, если вы сделаете object.method() и method будет перекрываться в B, то он будет вызывать этот метод перенастройки. Вы должны позаботиться о том, чтобы не сломать Liskov Substitution Principle. Вы можете получить доступ только к методам/свойствам и методам/свойствам. Это предпочтительный вариант, когда вам нужен только супертип.

B object = new B(); 

Вы создаете в ссылочной переменной типа B в B instance. Вы можете получить доступ только к методам/свойствам B и методам/свойствам родителей.

1

линия, как

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 в подписи.

Смежные вопросы