2009-11-23 44 views
20

Hãy nói rằng tôi có một lớp học như thế này (và cũng có thể tiếp tục giả định rằng tất cả các biến tin:Java: Bắt các thuộc tính của một lớp học để xây dựng một chuỗi đại diện

public class Item { 
    private String _id = null; 
    private String _name = null; 
    private String _description = null; 

     ... 
} 

Bây giờ, nếu tôi muốn xây dựng một toString() đại diện của lớp này, tôi sẽ làm một cái gì đó như thế này bên trong lớp Item:

@Override 
public String toString() { 
    return (_id + " " + _name + " " + _description); 
} 

Nhưng nếu tôi có nói 15 biến tư nhân bên trong lớp tôi có phải viết tên của mỗi và mọi? biến số như thế này?

Lý tưởng nhất, tôi muốn vượt qua với nhiệm vụ bằng cách duyệt qua danh sách các biến riêng của lớp này và xây dựng chuỗi đại diện:

@Override 
public String toString() { 
    ArrayList<String> members = getClass().getMembers(); //Some method like this 
    String string = ""; 
    for(...) 
     string += members[i] + " "; 
} 

Hoặc có lẽ là một phương pháp toJSON, tôi vẫn sẽ cần truy cập vào tên của các biến này. Bất kỳ đề xuất?

+0

Xem thêm [Auto-tạo toString Phương pháp] (http://stackoverflow.com/questions/2653268/auto -generating-tostring-method) – Vadzim

Trả lời

31

Bạn có thể làm:

@Override 
public String toString() { 
    StringBuilder sb = new StringBuilder(); 
    sb.append(getClass().getName()); 
    sb.append(": "); 
    for (Field f : getClass().getDeclaredFields()) { 
    sb.append(f.getName()); 
    sb.append("="); 
    sb.append(f.get(this)); 
    sb.append(", "); 
    } 
    return sb.toString(); 
} 

Đừng sử dụng dây nối để xây dựng một kết quả cuối cùng từ 15 thành viên dữ liệu, đặc biệt nếu toString() sẽ được gọi rất nhiều. Sự phân mảnh bộ nhớ và chi phí có thể rất cao. Sử dụng StringBuilder để tạo chuỗi động lớn.

Tôi thường nhận được IDE (IntelliJ) của mình chỉ đơn giản là tạo ra các phương thức toString() cho tôi thay vì sử dụng sự phản chiếu cho điều này.

Một cách tiếp cận thú vị là sử dụng @ToString annotation from Project Lombok:

import lombok.ToString; 

@ToString(excludes="id") 
public class ToStringExample { 
    private static final int STATIC_VAR = 10; 
    private String name; 
    private Shape shape = new Square(5, 10); 
    private String[] tags; 
    private int id; 

    @ToString(callSuper=true, includeFieldNames=true) 
    public static class Square extends Shape { 
    private final int width, height; 

    public Square(int width, int height) { 
     this.width = width; 
     this.height = height; 
    } 
    } 
} 

Tôi tìm thấy điều này nhiều hơn nữa thích hợp hơn để, nói rằng, các nhà xây dựng toString Jakarta Commons vì phương pháp này là xa cấu hình hơn và nó cũng được xây dựng tại biên dịch-thời gian không chạy.

+0

Cảm ơn bạn vì điều này. Tôi sẽ đi với cách tiếp cận đầu tiên tùy thuộc vào kết quả lược tả vì phương pháp thứ hai sử dụng một thư viện nữa. Tôi có thể phải viết toJSON cho chỉ 3-4 đối tượng vì vậy tôi sẽ để nó vào hồ sơ để quyết định. – Legend

+2

+1 cho thời gian biên dịch Ví dụ Lombok –

+0

Đối với Lombok, bạn chỉ cần thư viện trong khi biên dịch. Miễn là bạn không cần @SneakyThrows, thare không phụ thuộc vào thời gian chạy trong Lombok –

8

Kiểm tra API này org.apache.commons.lang.builder.ToStringBuilder, công cụ này cung cấp nhiều cách để tạo ra phản chiếu ngược dòng hoặc không phản ánh. Hãy xem các lớp con khác.

+1

Cảm ơn. Điều này trông rất thú vị. Đặc biệt là phương pháp không sử dụng sự phản chiếu. – Legend

2

Hầu hết các IDE cung cấp cách tạo phương thức toString trong một lớp nhất định.

Với một lớp Item với nhiều lĩnh vực:

class Item { 
    int i; 
    int j; 
    int k; 
    int l; 
    int m; 
    int n; 
    int o; 
} 

Ví dụ, trong Eclipse, biểu diễn "Generate toString()" tính năng trên lớp Item trên sẽ tạo điều sau đây:

@Override 
public String toString() { 
    return "Item [i=" + i + ", j=" + j + ", k=" + k + ", l=" + l + ", m=" 
      + m + ", n=" + n + ", o=" + o + "]"; 
} 

Sử dụng reflection sẽ cho phép một cách có lập trình để quan sát các trường của riêng mình, nhưng phản ánh chính nó là quá trình khá tốn kém (đọc: chậm), vì vậy trừ khi nó thực sự được yêu cầu, sử dụng phương thức cố định toString ritten tại thời gian chạy có lẽ sẽ được nhiều hơn mong muốn.

+0

Thực ra, lý do duy nhất tôi nghĩ đến Reflection là vì tôi cần viết một biểu diễn toString và toJSON. Tôi có thể viết chúng bằng tay nhưng đôi khi các lớp đại diện cho các mục từ cơ sở dữ liệu để chúng có thể có nhiều trường. Bạn vẫn đề xuất phương pháp thủ công? – Legend

+1

Nó thực sự sẽ đáp ứng các yêu cầu về hiệu năng của các phương thức toString và toJSON. Nếu hiệu suất không phải là một mối quan tâm, thì việc sử dụng sự phản chiếu có lẽ không quan trọng. Nếu hiệu suất là một mối quan tâm, có thể sẽ là một ý tưởng hay để lập hồ sơ mã bằng văn bản "thủ công" và mã được viết bằng cách sử dụng sự phản chiếu và xem sự khác biệt có quan trọng hay không. – coobird

+0

Đã hiểu. Cảm ơn lời giải thích. Tôi nghĩ tôi sẽ cần một buổi biểu diễn. Tôi đang viết một dịch vụ web REST vì vậy tôi đoán câu trả lời là khá rõ ràng từ lời giải thích của bạn. – Legend

1

Bạn có thể gặp vấn đề về tốc độ. Nếu bạn đang sử dụng sự phản chiếu, nó có thể rất chậm, so với việc chạy mã nguồn gốc.Nếu bạn định sử dụng sự phản chiếu, thì có lẽ bạn nên có một bộ nhớ đệm trong bộ nhớ của các biến bạn đang goaing để lặp lại.

2

Có một api như vậy, và nó được gọi Java Reflection

Để thực hiện những gì bạn đang yêu cầu, bạn chỉ có thể làm điều gì đó như:

Class<?> cls = this.getClass(); 
Field fieldlist[] = cls.getDeclaredFields(); 
for (Field aFieldlist : fieldlist) { 
    // build toString output with StringBuilder() 
} 
+0

Cảm ơn bạn vì điều đó. – Legend

4

này nên được chính xác những gì bạn đang tìm kiếm

public String toString() { 
    StringBuilder sb = new StringBuilder(); 
    try { 
     Class c = Class.forName(this.getClass().getName()); 
     Method m[] = c.getDeclaredMethods(); 
     Object oo; 

     for (int i = 0; i < m.length; i++) 
      if (m[i].getName().startsWith("get")) { 
       oo = m[i].invoke(this, null); 
       sb.append(m[i].getName().substring(3) + ":" 
         + String.valueOf(oo) + "\n"); 
      } 
    } catch (Throwable e) { 
     System.err.println(e); 
    } 
    return sb.toString(); 
} 
-1

Bắt đầu từ good medopal answer, bạn có thể sử dụng mã này để biểu thị chuỗi tất cả các trường của đối tượng, ngay cả khi bạn đang ở bên ngoài lớp học (rõ ràng là nó chỉ cho các lĩnh vực với một phương thức getter):

/**Gives a string representation of the fields of the object 
* @param obj the object 
* @return 
*/ 
private static String objectToString(Object obj) { 
    StringBuilder sb = new StringBuilder(); 
    try { 
     Class c = Class.forName(obj.getClass().getName()); 
     Method m[] = c.getDeclaredMethods(); 

     Object oo; 
     for (int i = 0; i < m.length; i++) 
      if (m[i].getName().startsWith("get")) { 
       oo = m[i].invoke(obj, null); 
       sb.append(m[i].getName().substring(3) + "=" 
         + String.valueOf(oo) + "\n"); 
      } 
    } catch (Throwable e) { 
     System.err.println(e); 
    } 
    return sb.toString(); 
} 
Các vấn đề liên quan