"Динамическое" Кастинг в Java - PullRequest
5 голосов
/ 30 декабря 2008

Привет всем,

Интересно, есть ли какие-нибудь хакеры Java, которые могут подсказать мне, почему следующее не работает:

public class Parent {
    public Parent copy() {
       Parent aCopy = new Parent();
       ...
       return aCopy;
    }
}

public class ChildN extends Parent {
    ...
}

public class Driver {
     public static void main(String[] args) {
         ChildN orig = new ChildN();
         ...
         ChildN copy = orig.getClass().cast(orig.copy());
     }
}

Код с удовольствием компилируется, но решает выбросить ClassCastException во время выполнения D =

Редактировать: Ух, очень быстрые ответы. Спасибо, парни! Так что, кажется, я не могу использовать этот метод для удешевления ... есть ли другой способ сделать это в Java? Я действительно думал о перезаписи каждого ChildN класса copy(), но не был в восторге от добавления дополнительного стандартного кода.

Ответы [ 7 ]

9 голосов
/ 30 декабря 2008

Это все равно что пытаться сделать это:

  public Object copy(){
       return new Object();
  }

А затем попытайтесь:

  String s = ( String ) copy();

Ваш Родительский класс и ChildN класс имеют такие же отношения, как Объект и Строка

Чтобы это работало, вам нужно сделать следующее:

public class ChildN extends Parent {
    public Parent copy() {
        return new ChildN();
    }
}

То есть переопределить метод "copy" и вернуть правильный экземпляр.


EDIT

Согласно вашему редактированию. Это на самом деле возможно. Это может быть один из возможных способов:

public class Parent {
    public Parent copy() {
        Parent copy = this.getClass().newInstance();
        //...
        return copy;
    }
}

Таким образом, вам не нужно переопределять метод «copy» в каждом подклассе. Это шаблон дизайна прототипа.

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

public class Parent {
    public Parent copy()  throws InstantiationException, IllegalAccessException  {
       Parent copy = this.getClass().newInstance();
       //...
       return copy;
    }
}
class ChildN  extends Parent {}

class Driver {
     public static void main(String[] args) throws  InstantiationException ,  IllegalAccessException  {
         ChildN orig = new ChildN();
         ChildN copy = orig.getClass().cast(orig.copy());
         System.out.println( "Greetings from : " + copy );
    }
}
6 голосов
/ 30 декабря 2008

Актер фактически пытается сделать это:

ChildN copy = (ChildN) orig.copy();

(Это отрабатывает приведение для выполнения во время выполнения, но это так и будет, потому что orig.getClass() будет ChildN.class). Однако orig.copy() не возвращает экземпляр ChildN, он возвращает экземпляр просто Parent, поэтому его нельзя привести к ChildN.

4 голосов
/ 30 декабря 2008

Если ChildN не переопределяет copy () для возврата экземпляра ChildN, то вы пытаетесь уменьшить объект типа parent до типа ChildN

2 голосов
/ 31 декабря 2008

Может быть, вам просто нужна копия / клон вашего объекта.

В этом случае реализуйте интерфейс Cloneable и при необходимости переопределите clone ().

public class Parent implement Cloneable {
   public Object clone() throws CloneNotSupportedException {
     Parent aCopy = (Parent) super.clone();
   ...
   return aCopy;
   }
}

public class ChildN extends Parent {
    ...
}

public class Driver {
     public static void main(String[] args) {
         ChildN orig = new ChildN();
         ...
         ChildN copy = orig.getClass().cast(orig.clone());
     }
}

Это именно то, что ваш метод "copy ()" пытался сделать способом Java.

(Да, вы могли бы также делать причудливые игры для самоанализа, но эти методы не работают или становятся уродливыми, как только у вас нет общедоступного конструктора по умолчанию. Клон работает в любом случае, независимо от того, какие у вас конструкторы.)

2 голосов
/ 30 декабря 2008

java.lang.Class # cast (Object) создает исключение ClassCastException, если Class # isInstance () возвращает false. Из Javadoc для этого метода:

Определяет, является ли указанный объект совместим с назначением с объектом представлены этим классом. Этот метод это динамический эквивалент Java язык instanceof оператора ... В частности, если это Class объект представляет собой объявленный класс, этот метод возвращает true если указано Object аргумент экземпляр представленного класса (или любого из его подклассов); это возвращается false в противном случае.

Поскольку Parent не является подклассом child, isInstance () возвращает false, поэтому cast () выдает исключение. Это может нарушать принцип наименьшего удивления, но он работает так, как задокументировано - cast () может только увеличивать, а не понижать.

1 голос
/ 01 января 2009

(Невозможно добавить код в комментарий, поэтому я добавлю сюда)

Относительно Cloneable: если вы внедряете Cloneable, реализуйте его следующим образом; намного чище звонить ...

public class Foo implements Cloneable {
    public Foo clone() {
        try {
            return (Foo) super.clone();
        } catch (CloneNotSupportedException e) {
            return null; // can never happen!
    }
}

[РЕДАКТИРОВАТЬ: Я также видел, как другие люди используют

throw new AssertionError("This should never happen! I'm Cloneable!");

в блоке catch.]

0 голосов
/ 04 января 2009

Причина, по которой downcasting не работает, заключается в том, что когда вы приводите родительский объект к дочернему типу, вы не можете вызывать методы дочернего типа для родительского объекта. Но работает по-другому ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...