2010-10-06 73 views
22

Tôi muốn ghi đè lên một phương thức trong một đối tượng được trao cho tôi bởi một nhà máy mà tôi có ít quyền kiểm soát hơn.Ghi đè một phương thức trong một đối tượng Java đã được khởi tạo

vấn đề cụ thể của tôi là tôi muốn ghi đè getInputStreamgetOutputStream của một đối tượng Ổ cắm để thực hiện dây khai thác gỗ.

Vấn đề chung chung như sau:

public class Foo { 
    public Bar doBar() { 
     // Some activity 
    } 
} 

Nơi tôi muốn có một khởi Foo và thay thế doBar với riêng tôi mà sẽ làm việc như sau:

Bar doBar() { 
    // My own activity 
    return original.doBar(); 
} 

Đối với Ổ cắm tôi sẽ trả lại một InputStreamOutputStream được bao bọc bằng cách ghi nhật ký để chặn dữ liệu.

+0

Java sẽ không cho phép bạn thực hiện chính xác điều đó. Tuy nhiên, bạn có thể xem xét kiểu trang trí. – zneak

Trả lời

15

Vì Java sử dụng OO dựa trên lớp, điều này là không thể. Những gì bạn có thể làm là sử dụng decorator pattern, tức là viết trình bao bọc cho đối tượng trả về luồng được bao bọc.

+0

Trang trí một ổ cắm là một nỗi đau thực sự, nhưng nó có vẻ là lựa chọn duy nhất của tôi. Cảm ơn. –

+0

hi tôi đã thử với trang trí nhưng thực sự đối tượng đang cố gắng trang trí có một số phương pháp được bảo vệ mà nó sử dụng trong nội bộ, tôi không thể ghi đè các phương pháp đó vì tôi sẽ phải gọi chúng từ trường hợp đối tượng của tôi. re protected – Turkish

+0

@YAT Bạn có thể nhìn vào sự phản chiếu. Nó thường được coi là hình thức xấu và khá hacky, nhưng nó có thể làm việc. Giống như: 'Method [] methods = object.getClass(). GetDeclaredMethods(); phương thức [0] .setAccessible (true); các phương thức [0] .invoke (đối tượng, arg1, arg2); ' – Erhannis

6

Bạn không thể thay thế các phương thức trong các đối tượng hiện có - bạn không thể thay đổi loại đối tượng hiện có, cho một điều.

Bạn có thể tạo phiên bản mới của một lớp khác mà giao cho phiên bản hiện tại, nhưng cũng có giới hạn.

Trong trường hợp thế giới thực của bạn là không có cách nào bạn có thể chỉ cần thực hiện một cuộc gọi riêng biệt để bọc các dòng trở lại bởi các ổ cắm? Bạn có thể cho biết thêm chi tiết.

0

Tôi không chắc chắn nếu điều này là có thể. Bạn đã xem xét việc tạo ra lớp của riêng bạn, có đối tượng được trả về bởi nhà máy như một thành viên, và sau đó viết phương thức doBar() cho lớp đó.

0

Bạn thực sự không thể thay đổi đối tượng khi đang chạy trong java.

Bạn có thể có thứ gì đó làm những gì bạn muốn bằng cách gói Foo của bạn vào một thiết bị tương tự khác sẽ ủy quyền mọi cuộc gọi tới Foo và cùng một bản ghi mọi thứ bạn muốn. (xem Proxy)

Nhưng nếu bạn muốn ghi nhật ký, có thể khía cạnh là lựa chọn tốt hơn. (Xem AspectJ)

0

hai lựa chọn:

  1. đơn giản: nếu Foo là bạn implemetn một giao diện, bạn có thể sử dụng một Dynamic proxy để thêm chức năng mới.
  2. công việc khác: những gì bạn có là lời khuyên "xung quanh" của AOP - bạn có thể sử dụng bất kỳ công cụ AOP hiện có nào để làm cho điều đó có thể. Spring Framework có thể làm điều đó cho bạn, nếu bạn đang sử dụng nó.
+2

-1 vì không rõ ràng, và để đưa mùa xuân vào điều này. –

11

Tôi nghĩ rằng có một cách để đạt được hiệu quả bạn muốn. Tôi thấy nó được sử dụng một cách hợp pháp khi xoay bằng các nút để cho phép lập trình viên tạo nút bấm làm điều gì đó khi nó được nhấn.

Giả sử bạn có lớp Foo của bạn:

public class Foo { 
    public Bar doBar() { 
    // Some activity 
    } 
} 

Sau đó, bạn có một lớp Á hậu hoặc một cái gì đó tương tự. Bạn có thể ghi đè lên phương thức doBar() tại điểm instantiation và nó sẽ chỉ ảnh hưởng đến đối tượng cụ thể đó.

rằng lớp có thể trông như thế này:

public class FooInstance{ 
    Foo F1 = new Foo(){ 
    public Bar doBar(){ 
     //new activity 
    } 
    } 

    Foo F2 = new Foo(); 

    F1.doBar(); //does the new activity 
    F2.doBar(); //does the original activity found in the class 
} 

Tôi không hoàn toàn chắc chắn rằng sẽ làm các trick cho bạn, nhưng có thể nó sẽ thiết lập bạn đi đúng hướng. Nếu không có gì khác có thể ghi đè lên một phương thức bên ngoài lớp, có thể điều đó sẽ giúp bạn.

+0

Cả hai sẽ thực hiện hoạt động ban đầu. Việc ghi đè bằng cách sử dụng các lớp ẩn danh chỉ hoạt động đối với các phương thức bên trong chính đối tượng đó. Đối với người gọi (từ bên ngoài đối tượng), bạn không thể sử dụng phiên bản ghi đè của phương thức. –

+1

Tôi thực sự chỉ thử nghiệm nó, và nó hoạt động. Không biết anh đang nói gì, Hazem. –

0

Một giải pháp liên quan đến proxy khác: bạn có thể sử dụng các khía cạnh để ghi đè phương thức trên một đối tượng cụ thể mà không tự mình phân lớp. Điều này đặc biệt thích hợp và phổ biến cho việc ghi nhật ký. Ví dụ này sử dụng spring-aop.

class Example { 

    final Foo foo; 

    Example(Foo original) { 
     AspectJProxyFactory factory = new AspectJProxyFactory(); 
     factory.setTarget(original); 
     factory.addAspect(FooAspect.class); 
     foo = (Foo) factory.getProxy(); 
    } 

    @Aspect 
    static class FooAspect { 

     @Before("execution(Foo.doBar())") 
     Object beforeDoBar() { 
      // My own activity 
     } 
    } 
0

Nếu Socket là giao diện thì bạn có thể tạo proxy động. Dưới đây là một ví dụ. Tôi đặt điều này ở đây trong trường hợp người khác cần phải làm điều này và thể hiện đích là một thể hiện của một giao diện.

Lý do chính điều này sẽ không hoạt động đối với Socket là vì java.lang.reflect.Proxy.newProxyInstance yêu cầu một mảng giao diện cho đối số thứ hai của nó, vì vậy các lớp sẽ không hoạt động ở đây. Như vậy cho ví dụ này tôi đã phải tạo một giao diện gọi là ParentInterface, chỉ có ba phương thức in.

public class Parent implements ParentInterface { 

    @Override 
    public void print1() { 
     System.out.println("parent 1"); 
    } 

    @Override 
    public void print2() { 
     System.out.println("parent 2"); 
    } 

    @Override 
    public void print3() { 
     System.out.println("parent 3"); 
    } 

    public static void main(String[] args) { 
     Parent originalInstance = new Parent(); 
     ParentInterface proxied = (ParentInterface) java.lang.reflect.Proxy.newProxyInstance(
       Parent.class.getClassLoader(), 
       new Class[]{ParentInterface.class}, 
       new ParentProxy(originalInstance)); 

     proxied.print1(); 
     proxied.print2(); 
     proxied.print3(); 

    } 

    static class ParentProxy implements InvocationHandler { 

     final Object realObject; 

     public ParentProxy(Object real) { 
      realObject = real; 
     } 

     @Override 
     public Object invoke(Object target, Method m, Object[] args) throws Throwable { 
      try { 
       if (m.getName().equals("print2")) { 
        print2(); 
        return null; 
       } else { 
        return m.invoke(realObject, args); 
       } 
      } catch (java.lang.reflect.InvocationTargetException e) { 
       throw e.getTargetException(); 
      } 
     } 

     public void print2() { 
      System.out.println("wrapper 2"); 
     } 

    } 

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