2015-06-11 18 views
5

Tôi muốn viết một loại mã an toàn. Đây là những gì tôi đã cố gắng:Viết một lớp học theo kiểu an toàn

public interface ResultTronsformer<T>{ 
    public T tranform(T t); 
} 

public class BigDecimalTransformer implements ResultTRansformer<BigDecimal>{ 

    public BigDecimal transform(BigDecimal t){ 
     return t.setScale(0); 
    } 
} 

Bây giờ tôi xác định giao diện Cột trông giống như

public interface Column{ 
    public ResultTransformer<?> getTransformer(); 
} 

và muốn sử dụng nó trong các phương pháp

public class Report{ 
    private Map<Column, Object> columnValuePairs; 

    public void putIntoACollection(Column c, Object columnsValue){ 
     ResultTransformer<?> rt = c.getTransformer(); 
     columnValuePairs.put(c, rt.transform(o)); //Error: Couldn't convert Object 
                //to the capture of wildcard 
    } 
} 

Làm thế nào tôi có thể sắp xếp lại thiết kế để đạt được loại an toàn mong muốn? Có lẽ tôi nên làm các loại kiểm tra tại thời gian chạy thay vì (ném một ngoại lệ)?

+1

Hệ thống kiểu Java không mạnh mẽ h để làm điều này. Nhưng bạn có thể sử dụng một số thư viện trình bao bọc để hiển thị ** API an toàn bên ngoài **. Ví dụ: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/ClassToInstanceMap.html – Max

+0

@Max Intersting, chưa được nghe về phương pháp đó. Có lẽ bạn có thể cung cấp một ví dụ như một asnwer? – user3663882

Trả lời

1

Bạn có thể thay đổi lớp Column và làm cho nó parametrized:

public interface Column<T> { 
    public ResultTransformer<T> getTransformer(); 
} 

Sau đó, bạn phải parametrize putIntoACollection phương pháp (không cần phải parametrize Report):

public class Report { 
    private Map<Column, Object> columnValuePairs; 

    public <T> void putIntoACollection(Column<T> c, T columnsValue) { 
     final ResultTransformer<T> rt = c.getTransformer(); 
     columnValuePairs.put(c, rt.transform(columnsValue)); 
    } 
} 

Bằng cách này, bạn không bao giờ cần sử dụng loại chụp.

Dưới đây là một ví dụ về cách bạn sẽ sử dụng nó:

private class BigDecimalColumn implements Column<BigDecimal> { 
    @Override 
    public ResultTransformer<BigDecimal> getTransformer() { 
     return new BigDecimalTransformer(); 
    } 
} 

public static void main(String[] args) { 
    final Report report = new Report(); 
    report.putIntoACollection(new BigDecimalColumn(), new BigDecimal("3.14")); 
} 
+0

Nhưng làm thế nào để bạn nghĩ rằng tôi có thể thực hiện các giao diện cột sau đó? Ví dụ, tôi cần 'BigDecimalColumn' mà lần lượt là một lớp con của' ResultTransformer '. Chúng tôi sẽ nhận được cảnh báo không được kiểm tra của trình biên dịch. – user3663882

+0

Bạn nói đúng. Tôi đã cập nhật câu trả lời của mình. –

+0

@ user3663882 câu hỏi này sẽ không thể giải quyết theo cách không tạo ra cảnh báo đó.Điều duy nhất cần làm là viết mã an toàn loại an toàn và thêm '@SuppressWarning (" unchecked ")'. – Max

1

Khi bạn nhận được máy biến áp, bạn cần phải xác định loại vì trình biên dịch sẽ không biết nó ở thời điểm đó.

Một giải pháp có thể là thêm lớp làm tham số vào getTransformer của cột và trả về một trình kết xuất chuyên biệt ResultTransformer.

public interface ResultTransformer<T> { 
    public T transform(T t); 
} 

public interface Column{ 
    public <T> ResultTransformer<T> getTransformer(Class<T> theClass); 
} 

public class Report{ 
    private Map<Column, Object> columnValuePairs; 

    public void putIntoACollection(Column c, Object o){ 
     ResultTransformer<Object> rt = c.getTransformer(Object.class); 
     columnValuePairs.put(c, rt.transform(o)); 
    } 

public interface ResultTransformer<T> { 
    public T transform(T t); 
} 

Một cách khác là tổng quát cột giao diện.

+0

Một lần nữa, việc thực hiện giao diện Cột sẽ không an toàn. Hay tôi không hiểu điều gì đó ....? – user3663882

+0

Bạn cũng có thể khái quát hóa giao diện Cột thành Cột nhưng điều đó sẽ ràng buộc các tham số kiểu Cột vào kết quả của ResultTransformer mà không phải là một phụ thuộc mà tôi sẽ chọn. Trong tương lai bạn có thể muốn có nhiều máy biến áp cho một Cột và điều đó sẽ không còn khả thi nữa. Nó phụ thuộc vào lớp nào sẽ chịu trách nhiệm xác định kiểu trả về của máy biến áp. Nếu đó là cột, hãy tạo Cột . Nếu nó là ResultTransformer thì đây là một cách tốt. – Maarten

2

Bạn có thể nghĩ về Cột giống như một loại container chứa loại cụ thể. Theo cách đó, bạn có thể giới thiệu kiểu generic trong khai báo Cột.

public interface Column<T>{ 
    public ResultTransformer<T> getTransformer(); 
} 

Sau đó, bạn có thể thay đổi Báo cáo phương pháp như sau:

public <T> void putIntoACollection(Column<T> c, T columnsValue){ 
     ResultTransformer<T> rt = c.getTransformer(); 
     columnValuePairs.put(c, rt.transform(columnsValue)); 
} 
+0

Chào mừng bạn đến với stackoverflow - đó là một bài đăng đầu tiên tuyệt vời! Tại chỗ trên. – Bohemian

+0

Điều này không giải quyết được sự cố vì bạn chỉ có thể chèn các cột của một loại. Thịt của vấn đề là các cột lưu trữ các loại khác nhau. – Max

+0

Tôi không phải là toàn bộ ngữ cảnh của tính năng báo cáo này nhưng tôi hiểu rằng Cột là một vùng chứa chứa một số giá trị. Bạn có thể tạo bất kỳ số cột nào cho từng loại được hỗ trợ. Giá trị trong cột có thể được chuyển đổi bởi ResultTransformer được tham chiếu. ResultTransformer này, như một thành phần an toàn kiểu, nên hoạt động trên cùng một kiểu dữ liệu mà cột lưu trữ. Nếu bạn sẽ có bất kỳ hệ thống phân cấp kiểu nào, thì một tệp ResultTransformer có thể được mở rộng để hoạt động trên nhiều loại trên cùng một cấp bậc. – ppro

0

Chúng ta có thể từ bỏ tất cả sự giả vờ, chỉ cần sử dụng thần nguyền rủa kiểu thô

 ResultTransformer rt = c.getTransformer(); 

Một giải pháp nhiều tính năng hơn -

static <T> T transform (ResultTransformer<T> rt, Object obj) 
    { 
     T t = (T)obj; // unchecked cast 
     return rt.transform(t); 
    } 


public void putIntoACollection(Column c, Object obj){ 
    ResultTransformer<?> rt = c.getTransformer(); 
    columnValuePairs.put(c, transform(rt, obj)); 

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