2011-02-20 39 views
24

Tôi đang làm việc với một số mã trong đó một đối tượng, "foo", đang tạo một đối tượng khác, "thanh" và chuyển nó Callable. Sau khi foo này sẽ trả về thanh và sau đó tôi muốn foo không thể truy cập được (ví dụ: có sẵn cho bộ sưu tập rác).Làm các lớp ẩn danh * luôn luôn * duy trì một tham chiếu đến cá thể kèm theo của chúng?

Suy nghĩ ban đầu của tôi là chỉ cần tạo một tên ẩn danh Callable. ví dụ:

class Foo { 
    ... 

    public Bar createBar() { 
    final int arg1 = ... 
    final int arg2 = ... 
    final int arg3 = ... 
    return new Callable<Baz>() { 
     @Override 
     public Baz call() { 
     return new Baz(arg1, arg2, arg3); 
     } 
    }; 
    } 
} 

Nó xảy ra với tôi rằng điều này có thể không thực sự làm việc như mong muốn, tuy nhiên, như một lớp bên trong thường giữ một tham chiếu đến đối tượng kèm theo của nó. Tôi không muốn tham chiếu đến lớp kèm theo ở đây, vì tôi muốn đối tượng kèm theo là được thu thập trong khi vẫn có thể truy cập được Callable.

Mặt khác, phát hiện các trường hợp kèm theo là không bao giờ thực sự được gọi nên được khá tầm thường, vì vậy có lẽ trình biên dịch Java là đủ thông minh để không bao gồm một tài liệu tham khảo trong trường hợp đó.

Vì vậy, ... một thể hiện của một lớp bên trong ẩn danh có giữ tham chiếu đối với trường hợp kèm theo của nó ngay cả khi nó không bao giờ thực sự sử dụng tham chiếu mẫu kèm theo?

Trả lời

22

Có, các phiên bản của các lớp bên trong ẩn danh giữ tham chiếu cho các trường hợp kèm theo của chúng ngay cả khi các tham chiếu này là chưa bao giờ thực sự được sử dụng. Mã này:

public class Outer { 
    public Runnable getRunnable() { 
    return new Runnable() { 
     public void run() { 
     System.out.println("hello"); 
     } 
    }; 
    } 
} 

Khi biên soạn với javac tạo ra hai tập tin lớp học, Outer.classOuter$1.class. Cách tháo rời thứ hai, lớp bên trong vô danh, với javap -c sản lượng:

Compiled from "Outer.java" 
class Outer$1 extends java.lang.Object implements java.lang.Runnable{ 
final Outer this$0; 

Outer$1(Outer); 
    Code: 
    0: aload_0 
    1: aload_1 
    2: putfield  #1; //Field this$0:LOuter; 
    5: aload_0 
    6: invokespecial #2; //Method java/lang/Object."<init>":()V 
    9: return 

public void run(); 
    Code: 
    0: getstatic  #3; //Field java/lang/System.out:Ljava/io/PrintStream; 
    3: ldc  #4; //String hello 
    5: invokevirtual #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    8: return 

} 

Dòng putfield cho thấy một tham chiếu đến dụ kèm theo là được lưu trữ trong các lĩnh vực this$0 (loại Outer) bởi các nhà xây dựng thậm chí mặc dù trường này không bao giờ được sử dụng nữa. Điều này là không may nếu bạn đang cố gắng để tạo ra các đối tượng tồn tại nhỏ có khả năng là với các lớp bên trong vô danh khi chúng giữ chặt vào ví dụ bao vây (có khả năng lớn). Giải pháp thay thế là sử dụng một thể hiện của một lớp tĩnh (hoặc một lớp cấp cao nhất) thay thế. Điều này thật đáng tiếc hơn.

+0

Chắc chắn sử dụng lớp tĩnh. Nó không phải là IMO dài dòng. –

+0

@deepc Bạn cần thêm các trường cho mỗi tham số, đó là 1 dòng cho mỗi tham số và một hàm tạo, có thêm 2 dòng cộng 1 cho mỗi tham số. Vì vậy, đối với một Runnable 3 đối số đơn giản, đó là ít nhất 8 dòng mã. Cú pháp lớp bên trong vô danh đã khá dài dòng so với cú pháp biểu thức lambda đúng: một lớp phương thức duy nhất có 4 dòng của bản mẫu mà bạn không cần với các biểu thức lambda. Vì vậy, một biểu thức lambda 1 dòng 1 tham số trong Java sẽ trở thành 13 dòng mã. Làm thế nào lớn nó sẽ được cho bạn để xem xét nó "mà tiết"? –

+0

bạn phù hợp với dòng "thống kê" của mình. Có lẽ đó là sở thích cá nhân. Nhưng sau khi tất cả phương thức 'call()' cũng phải ở trong đó. Và bây giờ chúng tôi có đủ mã để biện minh cho một lớp riêng biệt (không nhất thiết phải là một lớp cấp cao nhất khi bạn chỉ ra). Với tôi mã trông sạch hơn theo cách này. Thậm chí nhiều hơn như vậy nếu phương pháp đó dài hơn một vài dòng. –

1

Việc thay thế tĩnh (trong trường hợp này) không phải là lớn hơn nhiều (1 dòng):

public class Outer { 
    static class InnerRunnable implements Runnable { 
     public void run() { 
     System.out.println("hello"); 
     } 
    } 
    public Runnable getRunnable() { 
    return new InnerRunnable(); 
    } 
} 

BTW: nếu bạn sử dụng một Lambda trong Java8 sẽ không có lớp lồng nhau tạo ra. Tuy nhiên tôi không chắc chắn nếu các đối tượng CallSite mà được thông qua xung quanh trong trường hợp đó giữ một tham chiếu đến trường hợp bên ngoài (nếu không cần thiết).

5

Bạn có thể dễ dàng chuyển lớp ẩn danh lồng nhau thành lớp ẩn danh "tĩnh" bằng cách giới thiệu một phương thức tĩnh trong lớp của bạn.

import java.util.ArrayList; 


public class TestGC { 
    public char[] mem = new char[5000000]; 
    public String str = "toto"; 

    public interface Node { 
     public void print(); 
    } 

    public Node createNestedNode() { 
     final String str = this.str; 
     return new Node() { 
      public void print() { 
       System.out.println(str); 
      } 
     }; 
    } 

    public static Node createStaticNode(TestGC test) { 
     final String str = test.str; 
     return new Node() { 
      public void print() { 
       System.out.println(str); 
      } 
     }; 
    } 

    public Node createStaticNode() { 
     return createStaticNode(this); 
    } 

    public static void main(String... args) throws InterruptedException { 
     ArrayList<Node> nodes = new ArrayList<Node>(); 
     for (int i=0; i<10; i++) { 
      // Try once with createNestedNode(), then createStaticNode() 
      nodes.add(new TestGC().createStaticNode()); 
      System.gc(); 
      //Thread.sleep(200); 
      System.out.printf("Total mem: %d Free mem: %d\n", Runtime.getRuntime().totalMemory(), Runtime.getRuntime().freeMemory()); 
     } 
     for (Node node : nodes) 
      node.print(); 
     nodes = null; 
     System.gc(); 
     //Thread.sleep(200); 
     System.out.printf("Total mem: %d Free mem: %d\n", Runtime.getRuntime().totalMemory(), Runtime.getRuntime().freeMemory()); 
    } 
} 
+0

Đây không phải là câu trả lời cho câu hỏi, nhưng là một giải pháp tốt để viết một "lớp ẩn danh tĩnh" theo cách ít tiết kiệm hơn. Cảm ơn! –

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