2009-05-03 3 views
2

В Java, предположим, что у меня есть 3 класса, C простирается от B, который простирается от А.в Java Приведение к базовому типу

class X { 
    interface A {} 
    interface B extends A {} 
    interface C extends B {} 
    void f(A a) {} 

    void test() { 
     C c = new C() 
     B b = (B) c; 

     f(b); 
    } 
} 

Если я что-то вроде этого, как показано на test() выше:

C c = new C() 
B b = (B) c; 

f(b); 

f() принимает b как тип C с C и B оба простираются от A. Я хотел f() получить b как тип B, а не тип C.

Есть ли все-таки, чтобы заставить это рассвет?

ответ

9

f() будет всегда получает что-то напечатанное как A (несмотря на то, что под крышками это на самом деле B или C, и может быть соответствующим образом опущен).

Вы можете определить дополнительный п(), таким образом,

f(B b); 

и при необходимости

f(C c); 

и правильной будет называться в зависимости от класса аргумента. то есть компилятор определяет, какая функция вызывается в зависимости от типа аргумента. Это отличается от динамической отправки (или полиморфизма), которая будет возникать во время выполнения.

Обратите внимание, что ваш листинг в вопросе лишний. Вы можете написать:

C c = new C() 
B b = c; 

f(b); 

поскольку С простирается от B, C является Б.

1

Я хотел п получить б как тип B, а не типа С.

A C является B является A.

Внутри f, f видит только A части своего параметра a Если f вызывает публичную A функции, которая переопределяется в C, то C переопределения называется.

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

Если «C» и «B» должны иметь такое же поведение, не ovveride, что поведение в C.

Почему вы хотите это сделать?

+0

Это именно то, что я пытаюсь понять. Я хочу, чтобы параметр показывал поведение B. Я предполагаю, что мне нужно создать новый экземпляр B из C, прежде чем передать его f. Спасибо. – simao

+0

Если вы хотите, чтобы f работал на B, почему бы просто не создать B? Куда C входит? – tpdi

1

Ваш вопрос не имеет смысла. Что вы подразумеваете под «f принимает b как тип C»?

f принимает b как тип A, так как подпись метода говорит «A». Если вы вызываете методы на b, они будут вызываться на C, если C переопределяет их. Но это стандартное поведение в Java (все методы похожи на виртуальные методы на C++), и нет никакого способа его изменить.

Возможно, вы сможете описать свою проблему, и мы сможем помочь.

+0

Я хочу, чтобы f вызывал методы по параметру в зависимости от его типа, поэтому, если параметр является C, я хочу вызвать C.deliverTo(), но если параметр B, я хочу вызвать B.deliverTo(). Спасибо. – simao

2

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

Вы создаете объект (обозначенный ссылками c и b) типа C, и он будет оставаться a C, потому что невозможно изменить тип выполнения объекта и, следовательно, поведение; путем кастинга вы можете просто изменить свой тип времени компиляции, который влияет на то, как компилятор относится к нему.

Можете ли вы дать дополнительную информацию о конкретной проблеме, которую вы пытаетесь решить? Скорее всего, есть способ достичь вашей цели, изменив дизайн.

+0

Да, есть еще один способ сделать то, что я хочу, я могу создать новый экземпляр B из C, но я хотел понять, почему я не мог сделать это так, как я пробовал сначала :) Спасибо. – simao

0

Тот факт, что вы можете передать любой подкласс A в качестве параметра f (A a), присущ OO в Java, вы не можете обойти это. Если C расширяет A, вы можете всегда использовать его там, где ожидается A.

0

вы можете использовать отражение, чтобы проверить, если параметр класса А:

public void f(A a) { 
    if (a.getClass() == A.class) { 
    // ok, go on 
    } 
    else { 
    System.err.println("The parameter is not of class A!"); 
    } 
} 

не знаю, если это то, что вы хотите, но это может быть полезным.

0

Ваша проблема может быть решена путем понимания разницы между типом REFERENCE и INSTANCE. Когда вы ставите объект C как B, вы изменяете только ссылочный тип - фактический экземпляр объекта по-прежнему является объектом C - это должно быть совершенно очевидно, если вы посмотрите на свой объект в режиме отладки. Изменение ссылочного типа влияет только на то, как компилятор обрабатывает/воспринимает поведение кода - время выполнения не должно затрагиваться. Любой вызов метода на объект C всегда будет вызывать метод C (независимо от того, был ли объект передан другому объекту). Если вы переопределите метод из B и хотите вызвать версию метода B, вам необходимо создать экземпляр B.

0

Факт, что ваш код имеет ссылку на A, не означает, что объект, на который указывает, также является A. Если это C, он остается C. Ссылка в вашем источнике ограничивает доступные методы в вашем источнике. Кастинг необходим только потому, что иногда нужен метод другого типа, и нам нужно обмануть компилятор, чтобы мы начали использовать методы для типов каст. Естественно, что при попытке недействительности приведение может завершиться неудачно во время выполнения.

0
In upcasting and downcasting the object first upcast then downcastenter 
class A 
{ 
} 
class B extends A 
{ 
} 
Class M 
{ 
    psvm(String ar[]) 
{ 

    A a1= new B(); 
    B b2=(B)a1; 
} 
Смежные вопросы