Java-клонирование абстрактных объектов
Мне интересно, есть ли способ сделать следующее. У меня есть абстрактный класс, Shape
и все его разные подклассы, и я хочу переопределить метод clone. Все, что я хочу сделать в методе, - создать новый Shape
из toString()
текущего. Очевидно, я не могу сделать следующее, потому что Shape
является абстрактным. Есть ли другой способ сделать это, потому что переопределение клона в каждом подклассе просто для простого изменения имени кажется бесполезным.
public abstract class Shape {
public Shape(String str) {
// Create object from string representation
}
public Shape clone() {
// Need new way to do this
return new Shape(this.toString());
}
public String toString() {
// Correctly overriden toString()
}
}
Ответы
Ответ 1
Вы можете попытаться использовать отражение:
public abstract class AClonable implements Cloneable{
private String val;
public AClonable(){
}
public AClonable(String s){
val=s;
}
public String toString(){
return val;
}
@Override
public AClonable clone(){
try {
System.out.println(getClass().getCanonicalName());
AClonable b= getClass().getDeclaredConstructor(String.class).newInstance(val);
return b;
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
в методе clone() вы вызываете getClass(). Поскольку ACloneble ist abstract, вызов всегда будет идти в конкретный класс.
public class ClonebaleOne extends AClonable{
public ClonebaleOne(){
super();
}
public ClonebaleOne(String s) {
super(s);
// TODO Auto-generated constructor stub
}
}
и
public class ClonebaleTwo extends AClonable{
public ClonebaleTwo(){
super();
}
public ClonebaleTwo(String s) {
super(s);
// TODO Auto-generated constructor stub
}
}
и, наконец,
public static void main(String[] args){
AClonable one = new ClonebaleOne("One");
AClonable tow= new ClonebaleTwo("Two");
AClonable clone = one.clone();
System.out.println(clone.toString());
clone = tow.clone();
System.out.println(clone.toString());
}
Вывод:
ClonebaleOne
One
ClonebaleTwo
Two
Но это скорее взлом, чем решение
[EDIT] мои два клона были быстрее, чем;)
[EDIT] Будет полным. Еще одним внедрением clone() может быть
@Override
public AClonable clone(){
try {
ByteArrayOutputStream outByte = new ByteArrayOutputStream();
ObjectOutputStream outObj = new ObjectOutputStream(outByte);
ByteArrayInputStream inByte;
ObjectInputStream inObject;
outObj.writeObject(this);
outObj.close();
byte[] buffer = outByte.toByteArray();
inByte = new ByteArrayInputStream(buffer);
inObject = new ObjectInputStream(inByte);
@SuppressWarnings("unchecked")
Object deepcopy = inObject.readObject();
inObject.close();
return (AClonable) deepcopy;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
когда ваш абстрактный класс реализует Serialazable. Там вы записываете свой объект на диск и создаете копию со значением с диска.
Ответ 2
Вы не можете создать глубокий клон класса abstract
, потому что они не могут быть созданы. Все, что вы можете сделать, это мелкое клонирование с помощью Object.clone()
или возврата this
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
или
@Override
public Object clone() throws CloneNotSupportedException {
return this;
}
Абстрактный класс может выступать в качестве ссылки, и у него не может быть экземпляра, поэтому в этом случае будет выполняться мелкое клонирование
ИЛИ
В качестве лучшего подхода вы можете объявить clone()
как abstract
и попросить дочерний класс определить его, что-то вроде этого
abstract class Shape {
private String str;
public Shape(String str) {
this.str = str;
}
public abstract Shape clone();
public String toString() {
return str;
}
}
class Circle extends Shape {
public Circle(String str) {
super(str);
}
@Override
public Shape clone() {
return new Circle("circle");
}
}
Ответ 3
Хотя я сомневаюсь, что это хорошая идея, вы можете использовать отражение:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test {
public static void main(String[] args) {
Square s1 = new Square("test");
Square s2 = (Square) s1.clone();
// show that s2 contains the same data
System.out.println(s2);
// show that s1 and s2 are really different objects
System.out.println(s1 == s2);
}
public static abstract class Shape {
private String str;
public Shape(String str) {
this.str = str;
}
public Shape clone() {
try {
Class<?> cl = this.getClass();
Constructor<?> cons = cl.getConstructor(String.class);
return (Shape) cons.newInstance(this.toString());
} catch (NoSuchMethodException | SecurityException |
InstantiationException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return str;
}
}
public static class Square extends Shape {
public Square(String str) {
super(str);
}
}
}
Ответ 4
Вы можете разрешить с отражением:
public abstract class Shape {
private String str;
public Shape() {
}
protected Shape(String str) {
this.str = str;
}
public Shape clone() throws CloneNotSupportedException
{
try {
return (Shape)getClass().getDeclaredConstructor(String.class).newInstance(this.toString());
} catch (Exception e) {
throw new CloneNotSupportedException();
}
}
public String toString() {
return "shape";
}
public class Round extends Shape
{
public Round()
{
super();
}
protected Round(String str) {
super(str);
}
@Override
public String toString() {
return "round";
}
}
main(){
Shape round = new Round();
Shape clone = round.clone();
System.out.println(round);
System.out.println(clone);
}
но - ИМО - является плохой реализацией и подвержен ошибкам с большим количеством ям; лучшее использование Cloneable
и Object.clone()
- не использовать их! У вас есть много способов сделать то же самое (например, сериализация для глубокого клонирования) и мелкого клона, которые позволят вам лучше контролировать поток.