2011-08-03 37 views
13

Hãy xem xét các trường hợp sau đây:Tạo dụ từ dụ lớp cha

class A { 
    int x; 
    int y; 
} 

class B extends A { 
    int z; 
} 

Bây giờ, ở đâu đó trong các mã lớp học này được sử dụng như thế này:

A objA = getAFromSomewhere(); 
B objB = null; 

Và trong một tình huống nhất định tôi muốn làm một cái gì đó giống như

objB = objA; // can't do this 
objB.z = someZ; 

Tất nhiên các đối tượng thực sự phức tạp hơn một chút, vì vậy không chỉ sao chép hai i nts. Nhưng chúng cũng không quá phức tạp.

Tôi biết tôi có thể viết một constructor cho B như thế này:

public B(A anA) { 
    this.a = anA.a; 
    this.b = anA.b; 

    this.z = 0; 
} 

Nhưng nếu đó thực sự là cách duy nhất, tôi thích hợp nhất bổ sung thành viên B vào A.

cập nhật xem xét câu trả lời

Câu hỏi của tôi chưa đủ rõ ràng. Tôi hiểu rằng objB = objA; không thể làm việc (vì vậy tôi đã yêu cầu "một cái gì đó như", có nghĩa là một cái gì đó với sự phức tạp mã tương đương) và tôi biết về các vấn đề với các bản sao nông sâu.
Điều tôi đang tìm kiếm là khả năng sao chép các thành viên của một lớp cơ sở (giả sử sử dụng clone()). Bạn có thể hiểu rằng việc sao chép mọi thành viên theo cách thủ công là một giải pháp tồi vì nó làm tăng thêm độ phức tạp và sự dư thừa cho mã. Cảm ơn bạn đã trả lời!

Trả lời

12

Không có giải pháp nhỏ cho điều này vì không có giải pháp một kích thước phù hợp. Về cơ bản bạn không có tất cả thông tin trong một số B, vì vậy bạn không thể đảm bảo rằng bạn sẽ có đối tượng "hợp lý" B.

Bạn lẽ chỉ muốn tạo một constructor trong B mà phải mất một A và bản sao tất cả các dữ liệu A vào mới B.

+0

Tôi đã sửa đổi câu hỏi của mình để tính đến điều này. Có vẻ như thực sự không có giải pháp thanh lịch –

+1

@ didi_X8: Thật vậy - nhưng điều quan trọng là bạn hiểu * tại sao * không có giải pháp tổng quát thanh lịch. Ví dụ, nó sẽ có nghĩa là gì nếu tôi có thể viết: 'Object x =" Hello "; FileOutputStream stream = x; '? –

+1

"Bạn có thể chỉ muốn tạo một hàm tạo trong B, lấy A và sao chép tất cả dữ liệu A vào B. mới" Vâng, đó là chính xác những gì tôi muốn. Nhưng không nhắc đến mọi thành viên của A một cách riêng biệt. –

3

một giải pháp tầm thường (tương đối)!

Triển khai hàm tạo trong class B có phiên bản class A và sao chép các trường.

Một trong những lý do không có giải pháp chung trong ngôn ngữ chính là do sự cố sao chép sâu.

Ví dụ: nếu đối tượng nguồn chứa thêm Objects, trái ngược với các loại thuần túy, toán tử sao chép chung sẽ làm gì? Chỉ cần sao chép tài liệu tham khảo (tạo bản sao nông) hoặc tạo bản sao thực?

Điều gì sau đó nếu một trong các đối tượng đó là Collection? Nó cũng nên sao chép mọi phần tử của bộ sưu tập?

Kết luận logic duy nhất sẽ là thực hiện một bản sao nông, nhưng sau đó bạn chưa thực sự có một bản sao nào cả.

0

Tôi cũng bị sốc.:)

Bạn thực sự không thể thực hiện việc này: objB = objA;. Bởi vì Renault và BMW là những chiếc xe nhưng không phải tất cả các loại xe đều là BMW.

Cảm ơn về A as Car, B as BMW.

Bây giờ bạn nói:

Car car = new Renault(); 
BMV bmv = car; // you cannot do this. This is exactly your case. 
+1

Tôi hiểu rằng tôi không thể làm điều này, đó không phải là vấn đề. Tôi chỉ tự hỏi nếu không có cách đơn giản để nói (trong một nhà xây dựng): sao chép tất cả các chia sẻ (lớp cơ sở) các bộ phận từ obj "nguồn" (bất cứ điều gì nó được) và riêng biệt khởi tạo thêm (BMW). –

-3

Tôi nghĩ rằng cách tốt nhất là sử dụng một phương pháp nhà máy để tạo các đối tượng B từ A đối tượng.

class BFactory 
{ 
    public static B createB(A a) 
    { 
    B b = new B(); 
    copy(a,b); 

    return b; 
    } 

    private static <X,Y> void copy(X src,Y dest) throws Exception 
    { 
     List<Field> aFields = getAllFields(src.getClass()); 
     List<Field> bFields = getAllFields(dest.getClass()); 

     for (Field aField : aFields) { 
      aField.setAccessible(true); 
      for (Field bField : bFields) { 
       bField.setAccessible(true); 
       if (aField.getName().equals(bField.getName())) 
       { 
        bField.set(dest, aField.get(src)); 
       } 
      } 
     } 
    } 

    private static List<Field> getAllFields(Class type) 
    { 
     ArrayList<Field> allFields = new ArrayList<Field>(); 
     while (type != Object.class) 
     { 
      Collections.addAll(allFields, type.getDeclaredFields()); 
      type = type.getSuperclass(); 
     } 
     return allFields; 
    } 
} 
+2

Bị từ chối vì câu trả lời không tính đến câu hỏi: câu hỏi về cơ bản là "làm thế nào tôi có thể tránh đề cập đến từng thành viên A và B có điểm chung? –

0

... không phải vì đây là những gì mọi người nên làm nhưng nhiều hơn vì tôi có cảm giác như một thách thức, đây là một số mã kiểm tra mà không một bản sao đơn giản của các đối tượng (sử dụng setter và getter phương pháp):

import java.lang.reflect.Method; 
import org.junit.Test; 

public class ObjectUtils { 
    @Test 
    public void test() { 
     A a = new A(); 
     B b = new B(); 
     a.setX(1); 
     a.setY(2); 
     this.copyProperties(a, b); 
    } 
    private void copyProperties(Object obja, Object objb) { 
     Method m[] = obja.getClass().getDeclaredMethods(); 
     for(int i=0;i<m.length;i++) { 
      try { 
       String name = m[i].getName(); 
       if(name.startsWith("get") || name.startsWith("is")) { 
        Class rtype = m[i].getReturnType(); 
        String setter = name.replaceFirst("^(get|is)","set"); 
        Class s = objb.getClass(); 
        Method method = s.getMethod(setter,rtype); 
        Object[] args = new Object[1]; 
        args[0] = m[i].invoke(obja); 
        method.invoke(objb,args[0]); 
       } 
      } catch(Exception e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
    class A { 
     int x; 
     int y; 
     /** 
     * @return the x 
     */ 
     public int getX() { 
      return x; 
     } 
     /** 
     * @param x the x to set 
     */ 
     public void setX(int x) { 
      this.x = x; 
     } 
     /** 
     * @return the y 
     */ 
     public int getY() { 
      return y; 
     } 
     /** 
     * @param y the y to set 
     */ 
     public void setY(int y) { 
      this.y = y; 
     }  
    } 
    class B extends A { 
     int z; 
     /** 
     * @return the z 
     */ 
     public int getZ() { 
      return z; 
     } 
     /** 
     * @param z the z to set 
     */ 
     public void setZ(int z) { 
      this.z = z; 
     } 
    } 
} 
0

lẽ bạn có thể làm điều này:

class A { 
    int x; 
    int y; 

    A(A a) { 
     this.x = a.x; 
     this.y = a.y; 
    } 
} 

class B extends A { 
    int z; 

    B(A a) { 
     super(a); 
     z = 0; 
    } 
} 

bạn vẫn đang liệt kê tất cả các lĩnh vực, nhưng chỉ một lần mỗi lớp.

3

Nếu bạn không sợ commons-beanutils bạn có thể sử dụng PropertyUtils

import org.apache.commons.beanutils.PropertyUtils; 
class B extends A { 
B(final A a) { 
try { 
     PropertyUtils.copyProperties(this, a); 
    } 
    catch (Exception e) { 
    } 
} 
} 
0

Nếu bạn thay đổi phương pháp của bạn để tạo B đối tượng, bạn chỉ có thể làm những gì bạn muốn sử dụng:

objB = (B) objA; 
objB.z = someZ; 

Điều này thậm chí có thể được gạch chân, nhưng bạn cần dấu ngoặc đơn:

((B) objA).z = someZ; 

Nếu không, bạn phải đi đường dài bằng cách sử dụng các hàm tạo:

objB = new B(objA); 
objB.z = someZ; 

Trong trường hợp này, tôi khuyên bạn nên sao chép các trường của siêu lớp trong siêu lớp. Khác, nếu bạn thêm một trường vào lớp đó sau, bạn có thể quên thay đổi việc sao chép dễ dàng hơn.

class A { 
    int x; 
    int y; 
    public A(A objA) { 
     x = objA.x; 
     y = objA.y; 
    } 
} 

class B extends A { 
    int z; 
    public B(A objA) { 
     super(objA); 
    } 
} 

tôi thích hợp nhất bổ sung thành viên B vào A.

Bạn thể làm điều này nếu lớp học của bạn AB chia sẻ cùng một package hoặc nếu các biến trong lớp A của bạn được khai báo là protected. Sau đó, bạn chỉ có thể truy cập vào các trường của siêu lớp.

class A { 
    protected int x; 
    protected int y; 
} 

class B extends A { 
    int z; 

    void merge(A a){ 
    super.x = a.x; 
    y = a.y;  // you do not *need* to use the super keyword, but it is a good hint to 
        // yourself if you read your program later and might wonder ‘where is 
        // that y declared?’ 
    } 
} 

năng bảo mật bằng, tất nhiên, là:

objB = new B(); 
objB.merge(objA); 
objB.z = someZ; 
+0

Bạn không thể bỏ lớp cha lên lớp con. – Line

+0

" Nếu bạn thay đổi phương thức của bạn để tạo các đối tượng 'B'…” biến có thể được khai báo là 'A' nhưng bạn vẫn có thể truy cập các thành viên của' B' bằng cách sử dụng các phôi. – Paramaeleon

1

Nếu bạn không cần chức năng đầy đủ của A, đó cũng là một tùy chọn để tạo ra lớp B, tổ chức sao chép nội của A dụ và thực hiện một số tập con tối thiểu của các phương thức thông qua giao diện C bằng cách ủy quyền chúng thành cá thể.

class A implements IC { 
    int x; 
    int y; 

    public C() { 
    ... 
    } 
} 

class B implements IC { 
    private A _a; 

    public B(A a) { 
    _a = a; 
    } 

    public C() { 
    _a.C(); 
    } 
} 
0

Giả sử rằng lớp học của bạn A có một phương thức setter và getter quy ước đặt tên rất gọn gàng và sạch sẽ như setXXX(Object xxx) và corrresponding getXXX() mà trả về điều tương tự (Object xxx) như là một param truyền cho setXXX()

tôi đã viết phương thức tiện ích bằng cách sử dụng phản chiếu

public static B createSubclassInstance(A a) throws SecurityException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ 
     Method[] aMethods = Class.forName("package.A").getDeclaredMethods(); 
     B b = new B(); 
     for (Method aMethod : aMethods) { 
      String aMethodName = aMethod.getName(); 
      Class param = aMethod.getReturnType(); 
      if (methodName.startsWith("get")){ 
       String setterMethodName = methodName.replaceFirst("get", "set"); 
       Method bMethod = Class.forName("package.B").getMethod(setterMethodName); 
       Object retValA = aMethod.invoke(a,null); 
       bMethod.invoke(b,retValA); 
      } 

     } 
     return b; 
    } 
Các vấn đề liên quan