2015-04-20 17 views
6

Hãy lấy một ví dụ để làm cho nó dễ dàng hơn. Tôi xây dựng một danh sách mà nhà xây dựng có một số integer và một số List<Integer>. Danh sách của tôi sẽ chứa tất cả các phần tử của danh sách đã cho nhân với integer. danh sách của tôi không lưu trữ các yếu tố mới nhưng tính họ một cách nhanh chóng:Cách trả lại một đối tượng có nhiều loại

class MyList extends AbstractList<Integer> implements RandomAccess { 
    private final int multiplier; 
    private final List<Integer> list; 

    public MyList(int multiplier, List<Integer> list) { 
     this.multiplier = multiplier; 
     this.list = list; 
    } 

    @Override 
    public Integer get(int index) { 
     return list.get(index) * multiplier; 
    } 

    @Override 
    public int size() { 
     return list.size(); 
    } 
} 

Sau đó, chúng ta có thể gọi new MyList(3, list) với list = [0, 1, 2, 3] để có được [0, 3, 6, 9].

Tôi muốn giới hạn nhà phát triển cung cấp cho nhà xây dựng MyList danh sách cũng là RandomAccess, để chắc chắn anh ấy sẽ không hủy hoại buổi biểu diễn.

Tôi cố gắng để thay đổi các nhà xây dựng với:

public <E extends List<Integer> & RandomAccess> MyList(int multiplier, E list) 

MyList không phải là vấn đề nhưng bây giờ chúng ta không thể gọi các nhà xây dựng mà không sử dụng một thực hiện của cả hai List<Integer>RandomAccess như ArrayList<Integer>. Vì vậy, một người có danh sách này: List<Integer> list = new ArrayList<>(); không thể làm new MyList(3, list); (Vì nó được khai báo với List<Integer> thay vì ArrayList<Integer>).

Các giải pháp khác mà tôi có là một trong những điều này:

public MyList(int multiplier, List<Integer> list) { 
     if(!(list instanceof RandomAccess)) { 
      // Do something like log or throw exception 
     } 
     this.multiplier = multiplier; 
     this.list = list; 
    } 

Nhưng bây giờ tôi không thể kiểm tra tại thời gian biên dịch nếu danh sách thực hiện RandomAccess, và tôi cần phải sử dụng instanceof và tôi ghét làm điều này.

Tôi chắc rằng có một cách tốt hơn nhưng nó là gì?

+0

Hãy thử sử dụng | (hoặc toán tử) thay vì & (và toán tử). Tôi không chắc chắn nếu điều này sẽ làm việc, nhưng nó có giá trị một thử. – MLavrentyev

+0

Vì vậy, nếu bạn muốn giới hạn nhà phát triển thành 'RandomAccess' _" để chắc chắn anh ta sẽ không làm hỏng hiệu suất "_, thì tại sao bạn vẫn muốn cho phép họ gọi hàm tạo của bạn với' List', rõ ràng là không thực hiện 'RandomAccess'? Đây có phải là lý do thẩm mỹ không? –

+0

Tôi cần phải gọi 'List.get()'. – Happy

Trả lời

1

Bạn có thể áp dụng các giải pháp sử dụng bởi Collections.unmodifiableList. Thay vì một hàm tạo công khai, có một phương thức tĩnh trả về một trong hai cách triển khai thực hiện, một phương thức triển khai thực hiện RandomAccess, phương thức kia thì không.

Đây là mã cho Collections.unmodifiableList.

public static <T> List<T> unmodifiableList(List<? extends T> list) { 
    return (list instanceof RandomAccess ? 
      new UnmodifiableRandomAccessList<>(list) : 
      new UnmodifiableList<>(list)); 
} 

Tôi biết bạn đã nói bạn không thích sử dụng instanceof. Tôi cũng vậy, nhưng đôi khi đó là điều tốt nhất để làm.

Lưu ý rằng các giải pháp sử dụng các nhà xây dựng

public <E extends List<Integer> & RandomAccess> MyList(int multiplier, E list) 

không chỉ xấu xí, ở chỗ nó buộc các lập trình viên để cast (ví dụ như để một ArrayList), nhưng nó sẽ không thực sự làm việc. Ví dụ: nếu list là một phiên bản của Collections$UnmodifiableRandomAccessList, thậm chí không thể truyền cho một loại triển khai cả hai ListRandomAccess, bởi vì Collections$UnmodifiableRandomAccessList là riêng tư.

0

Nếu lớp bạn cần có một danh sách truy cập ngẫu nhiên, sau đó lớp học của bạn nên đối phó với điều đó và không đẩy nhu cầu của lớp học của bạn vào người gọi.Hơn nữa, nó sẽ đơn giản hơn để làm phép nhân trong các nhà xây dựng - bạn phải làm điều đó tại một số điểm, nhưng làm nó sớm có nghĩa là bạn có thể vứt bỏ rất nhiều mã:

class MyList extends ArrayList<Integer> { 

    public MyList(int multiplier, List<Integer> list) { 
     for (Integer i : list) 
      add(i * multiplier); 
    } 
} 

Đó là tất cả bạn cần, an toàn hơn: với sự tuyệt đối của bạn, cả danh sách và người gọi của bạn đều có tham chiếu đến danh sách. Nếu sau khi gọi hàm dựng, cuộc gọi thay đổi danh sách, mã khác sử dụng danh sách nhân sẽ bất ngờ thấy các giá trị trong danh sách thay đổi.

+0

Giả định của bạn rằng 'bạn phải làm điều đó tại một số điểm' cũng có thể là sai. Đó là hoàn toàn có thể rằng đây là một danh sách của nhiều mặt hàng, trong đó rất ít người thực sự đã từng truy cập (do đó cần phải truy cập ngẫu nhiên). – sprinter

+0

@sprinter Tôi vừa thực hiện kiểm tra hiệu năng: chạy mã này với kích thước danh sách đầu vào gồm 10 số ngẫu nhiên mất ít hơn 5 micro giây (trên PC trung bình, sau khi khởi động) để tạo danh sách số nhân mới - đó là 0,00005005 giây cho mỗi thành phần. Tôi không biết về bạn, nhưng tôi có thể sống với "hit hiệu suất" đó để tẩy sạch tất cả mã đó. – Bohemian

+0

Bởi nhiều mặt hàng tôi có nghĩa là nhiều triệu. Thành thật mà nói nếu bạn có 10 mục trong danh sách thì cấu trúc dữ liệu không quan trọng lắm. – sprinter

1

Tôi khuyên bạn nên sử dụng instanceof. Trên thực tế đây là chính xác những gì các tài liệu RandomAccess gợi ý: danh sách các thuật toán

Generic được khuyến khích để kiểm tra xem danh sách cho trước là một instanceof giao diện này trước khi áp dụng một thuật toán mà sẽ cung cấp hiệu suất kém nếu nó được áp dụng cho một tuần tự danh sách truy cập và thay đổi hành vi của chúng nếu cần thiết để đảm bảo hiệu suất có thể chấp nhận được .

Nhà xây dựng của bạn có thể có khả năng có hai triển khai. Nếu RandomAccess được thực hiện sau đó nó sẽ lưu một tham chiếu đến List nếu không nó sẽ tạo ra một mới ArrayList và bản sao tất cả các yếu tố để nó:

class MyList { 
    private final int multiplier; 
    private final List<Integer> list; 

    public MyList(int multiplier, List<Integer> list) { 
     this.multiplier = multiplier; 
     if (list instanceof RandomAccess) 
      this.list = list; 
     else 
      this.list = new ArrayList<>(list); 
    } 

    public int get(int index) { 
     return multiplier * list.get(index); 
    } 
} 
+0

Câu trả lời của bạn và pbabcdefp là đủ tốt cho tôi. Tôi lưu trữ một số dữ liệu nếu tôi chỉ cần. Vì vậy, tôi muốn ngăn chặn các nhà phát triển, ông nên sử dụng một danh sách mà thực hiện RandomAccess. Có một cách lập trình để làm điều này? Hoặc thay thế duy nhất tôi có là javadoc? – Happy

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