2016-12-20 8 views
2

У меня проблема с подклассификацией очень простого класса, у которого есть методы, возвращающие исходный класс.Использование метода, возвращающего объект MyClass в MyClass, когда MySubClass расширяет MyClass

public class MyClass { 

    public MyClass(){ 
    } 

    public MyClass filterOn(String something){ 
     MyClass result=new MyClass(); 
     result.doSomethingUsingThisInstance(this, something); 

     return result; 
    } 

} 


public class MySubClass extends MyClass{ 
    .... 
} 

Хорошо, теперь, если я хочу, чтобы назвать это:

MySubClass subClass=new MySubClass(); 
    MySubClass subClass2=(MySubClass)subClass.filterOn("Hello World"); 

Тогда у меня есть java.lang.ClassCastException: не может бросить MyClass к MySubClass

Как это предотвратить?

+0

Возвращаемое значение 'filterOn' является' MyClass', а не 'MySubClass', поэтому исключение действительно достаточно. Измените тип 'result', чтобы исправить его. – Henrik

+0

'MyClass result = new MyClass();' в вашем методе фильтра есть проблема. вы создаете супер-экземпляр и возвращаетесь в конце, вы не можете использовать его в подтипе. Возможно, вы захотите переопределить метод filterOn в MySubClass. – Kent

+0

Простейшим решением было бы не использовать 'subClass2', поскольку это не« MySubClass ». Если вам нужен экземпляр 'MySubClass', чем просто перезаписать' filteron' и вернуть экземпляр 'MySubClass' вместо – n247s

ответ

2

Override метод filterOn() для создания экземпляра вы хотите в MySubClass:

public class MySubClass extends MyClass{ 

    public MyClass filterOn(String something){ 
     MySubClass result = new MySubClass(); 
     result.doSomethingUsingThisInstance(this, something); 
     return result; 
    } 
    .... 
} 

Вы также могли бы избежать дублирования в методе filterOn() путем введения метода в MyClass для создания экземпляра текущего класса, который мы переопределяем в подклассе:

public class MyClass { 

    public MyClass(){ 
    } 

    public MyClass createSpecificInstance() { 
    return new MyClass(); 
    } 

    public MyClass filterOn(String something){ 
     MyClass result = createSpecificInstance(); 
     result.doSomethingUsingThisInstance(this, something); 

     return result; 
    } 

} 

Теперь подклассов переопределить только createSpecificInstance():

public class MySubClass extends MyClass { 

    public MyClass createSpecificInstance() { 
    return new MySubClass(); 
    } 

} 
+0

Простое решение: D. Будет ли способ объявить мои методы, используя какой-то трюк «дженериков» (который я вообще не освою)? – Myoch

+0

Действительно :) Да, вы могли бы, но вы должны использовать отражение, если хотите избежать псевдо-заводского метода 'createSpecificInstance()', – davidxxx

+0

Не могли бы вы объяснить немного больше, пожалуйста? вы говорите, что я могу использовать generics (но как?), только если я использую отражение (например, используя класс Method?), правильно? – Myoch

0
(MySubClass)subClass.filterOn("Hello World"); 

и

public MyClass filterOn(String something) 

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

прочитать: java.lang.ClassCastException

0

Вы пытаетесь бросить свой возвращаемое значение производного типа, который не работает и приводит за исключением класса литым.

Причина: Выполняется листинг вверх в иерархии типов, поскольку ваш базовый класс требует только атрибутов/методов, которые имеет производный класс (тривиально). Другой способ не работает, так как это может привести к проблемам. Рассмотрим:

class Base { 
// some stuff here 
} 

class Derived1 extends Base { 
    private int foo; 
} 

class Derived2 extends Base { 
    private String bar; 
} 

Base myObject = new Derived1(); 
// this works, Derived1 has everything Base requires 
// myObject.foo still exists, but can not be trivially accessed. 

Derived2 myOtherObject = (Derived2)myObject; 
// Now what? 
// What happens to myObject.foo? 
// Where does myOtherObject.bar come from? 

Что вы можете сделать в вашей ситуации:

  • Если реализации filterOn() сильно отличаются в зависимости от конкретной реализации производного класса, сделать это в абстрактную базового класса и повторно реализовать его в производном классе.
  • Проверьте, есть ли у вас проблемы с дизайном. Вам действительно нужно передать результат filterOn() в производный класс?
  • Использование Generics. Помните, что вы можете использовать производный класс как общий в базовом классе. Это работает только в том случае, если реализация filterOn() точно такая же для каждого подкласса (за исключением типов курсов).
  • Предоставить конструктор, который позволяет создавать экземпляр производного класса из базового класса. Используйте его вместо броска.Что-то в строке
Derived1(Base baseObject){ 
    // copy all the stuff from the base object 
    this.foo = 0; // initialize the rest 
} 
  • Возможно наследование не то, что вам нужно. Композиция имеет тенденцию часто бить наследование (Explained nicely here). Таким образом, вы можете попробовать, что:
class BetterThenDerived { 
    private Base myBaseObject; 
    private int foo; 
} 
0

Это сторона проблемы ковариации. См. Например, Covariance and contravariance. И Demonstrate covariance and contravariance in Java?. На самом деле Java не отличается, но есть несколько вариантов.

Во-первых, при переопределении метода вы можете объявить более конкретный тип возвращаемого значения. Например, в MySubClass вы можете объявить:

@Override 
public MySubClass filterOn(String something) { 
    // ... 
} 

Теперь это не решает проблему. Вам все еще нужен способ для этого метода создать и сделать что-то для объекта MySubClass. Что он делает, он освобождает код клиента от необходимости приведения вообще. Вы можете взять реализацию ответа метода отправки формы davidxxx (при условии, что result.doSomethingUsingThisInstance() является protected или public):

@Override 
public MySubClass filterOn(String something) { 
    MySubClass result = new MySubClass(); 
    result.doSomethingUsingThisInstance(this, something); 
    return result; 
} 

Вы можете быть раздражены, чтобы дублировать этот метод во всех вас подклассах. Если реальная работа остается в result.doSomethingUsingThisInstance(), я думаю, вы сможете жить с ней.

Другая идея состоит в том, чтобы clone() для создания объекта нужного типа исполнения:

public class MyClass implements Cloneable { 

    public MyClass filterOn(String something) { 
     try { 
      MyClass result = (MyClass) this.clone(); 
      result.doSomethingUsingThisInstance(this, something); 

      return result; 
     } catch (CloneNotSupportedException cnse) { 
      throw new AssertionError(cnse); 
     } 
    } 

} 

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

public class MySubClass extends MyClass { 

    @Override 
    public MySubClass filterOn(String something) { 
     return (MySubClass) super.filterOn(something); 
    } 

} 

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

  • Нет связанных вопросов^_^