Ява; класс базового класса для производного класса
Почему я не могу отнести экземпляр базового класса к производному классу?
Например, если у меня есть класс B, который расширяет класс C, почему я не могу это сделать?
B b=(B)(new C());
или это?
C c=new C();
B b=(B)c;
Хорошо, позвольте мне уточнить, что я пытаюсь сделать. Вот что у меня есть:
public class Base(){
protected BaseNode n;
public void foo(BaseNode x){
n.foo(x);
}
}
public class BaseNode(){
public void foo(BaseNode x){...}
}
Теперь я хочу создать новый набор классов, которые расширяют Base и Basenode, например:
public class Derived extends Base(){
public void bar(DerivedNode x){
n.bar(x);//problem is here - n doesn't have bar
}
}
public class DerivedNode extends BaseNode(){
public void bar(BaseNode){
...
}
}
Поэтому по существу я хочу добавить новые функции в Base и BaseNode, расширив их оба и добавив функцию к обоим. Кроме того, Base и BaseNode должны быть доступны сами по себе.
Мне бы очень хотелось сделать это без дженериков, если это возможно.
Хорошо, поэтому я закончил выяснять это, отчасти благодаря ответу Маруса Перри.
В моем конструкторе для Base
, n
создается как BaseNode
. Все, что мне нужно было сделать, - это создать экземпляр n
как DerivedNode
в моем производном классе в конструкторе, и он отлично работает.
Ответы
Ответ 1
Вам нужно использовать ключевое слово instanceof, чтобы проверить тип объекта, на который ссылается n, и придумать объект и вызвать строку () метод. Оформить заказ Derived.bar() ниже.
public class Test{
public static void main(String[] args){
DerivedNode dn = new DerivedNode();
Derived d = new Derived(dn);
d.bar( dn );
}
}
class Base{
protected BaseNode n;
public Base(BaseNode _n){
this.n = _n;
}
public void foo(BaseNode x){
n.foo(x);
}
}
class BaseNode{
public void foo(BaseNode x){
System.out.println( "BaseNode foo" );
}
}
class Derived extends Base{
public Derived(BaseNode n){
super(n);
}
public void bar(DerivedNode x){
if( n instanceof DerivedNode ){
// Type cast to DerivedNode to access bar
((DerivedNode)n).bar(x);
}
else {
// Throw exception or what ever
throw new RuntimeException("Invalid Object Type");
}
}
}
class DerivedNode extends BaseNode{
public void bar(BaseNode b){
System.out.println( "DerivedNode bar" );
}
}
Ответ 2
потому что если B продолжит C, это означает, что B является C, а не C является B.
переосмыслить то, что вы пытаетесь сделать.
Ответ 3
Существующие ответы хороши в терминах абстрактного аргумента, но я хотел бы сделать более конкретным. Предположим, вы можете это сделать. Затем этот код должен был бы скомпилироваться и запускаться:
// Hypothetical code
Object object = new Object();
InputStream stream = (InputStream) object; // No exception allowed?
int firstByte = stream.read();
Где именно возникла бы реализация метода read
? Это абстракция в InputStream
. Откуда он получит данные? Просто нецелесообразно рассматривать голый java.lang.Object
как InputStream
. Это намного лучше для броска, чтобы выбросить исключение.
По моему опыту, сложно получить "параллельные иерархии классов", подобные тому, который вы описываете для работы. Вы можете обнаружить, что дженерики помогают, но они могут получить волосатые очень быстро.
Ответ 4
Вы можете создать конструктор для B, который принимает C как параметр.
См. этот пост для идей делать то, что вы пытаетесь сделать.
Ответ 5
Вы не можете этого сделать, потому что C не обязательно реализует поведение, которое вы создали, когда вы расширили его в B.
Итак, скажем, C имеет метод foo()
. Затем вы знаете, что вы можете вызывать foo()
на B, так как B расширяет C, поэтому вы можете соответствующим образом применить обработку B, как если бы это был C с (C)(new B())
.
Однако - если B имеет метод bar()
, ничто в отношении подкласса говорит, что вы можете называть bar()
на C тоже. Таким образом, вы не можете лечить C, как если бы это был B, и поэтому вы не можете использовать.
Ответ 6
В вашем примере вы можете использовать n в DerivedNode, если вы уверены, что n является экземпляром DerivedNode или вы можете использовать generics:
public class Base<N extends BaseNode> {
protected N n;
public void foo(BaseNode x){
n.foo(x);
}
}
public class BaseNode {
public void foo(BaseNode x){...}
}
public class Derived extends Base<DerivedNode> {
public void bar(DerivedNode x){
n.bar(x); // no problem here - n DOES have bar
}
}
public class DerivedNode extends BaseNode {
public void bar(BaseNode){
...
}
}
Ответ 7
Базовые классы не должны знать ничего о связанных с ними классах, в противном случае возникнут проблемы, отмеченные выше. Downcasting - это "запах кода", а понижение в базовом классе до производного класса особенно "вонючее". Такие конструкции могут также затруднить решение круговых зависимостей.
Если вы хотите, чтобы базовый класс использовал реализации производных классов, используйте шаблон шаблона шаблона. Добавляем виртуальный или абстрактный метод в ваш базовый класс и переопределяем его и реализуем в производном классе. Затем вы можете безопасно называть это из базового класса.
Ответ 8
Потому что, если B extends C
, тогда B может иметь материал, который не находится в C (например, переменные экземпляра, которые вы инициализируете в конструкторе, которые не находятся в новом C())
Ответ 9
Простой и подробный ответ с наглядными примерами: http://www.mathcs.emory.edu/~cheung/Courses/171/Syllabus/2-OOP/convert.html