2012-02-22 60 views
8

Hãy xem xét các nhà xây dựng sau cho lớp Foo (mà vì lợi ích của sự rõ ràng là không một lớp generic):Trường hợp sử dụng cho một nhà xây dựng chung là gì?

public <T> Foo(T obj) { } 

Đây là cú pháp hợp lệ cho nhà thầu, giống như với bình thường generic methods.

Nhưng việc sử dụng cú pháp này là gì? Thông thường các phương thức chung cung cấp kiểu an toàn cho kiểu trả về của chúng và có thể được hưởng lợi từ kiểu suy luận của trình biên dịch. Ví dụ:

Pair<String, Integer> stringInt = Pair.of("asfd", 1234); 

Nhưng cuộc gọi tới hàm tạo luôn trả về một thể hiện của loại khai báo. Các nhà xây dựng trên chỉ có thể được thay thế bằng erasure của nó:

public Foo(Object obj) { } 

Trong Generics Tất nhiên là không chỉ về loại an toàn với nhiều loại trở lại. Các nhà xây dựng chỉ có thể muốn hạn chế các loại lập luận (s) được thông qua tại Tuy nhiên, các lý do trên vẫn được áp dụng cho một số loại bị chặn:.

public <N extends Number> Foo(N number) { } 

public Foo(Number number) { } //same thing 

tham số kiểu Ngay cả lồng nhau với giới hạn được xử lý sử dụng ký tự đại diện:

public <N extends Number, L extends List<N>> Foo(L numList) { } 

public Foo(List<? extends Number> numList) { } //same thing 

Vì vậy, trường hợp sử dụng hợp pháp để có một nhà xây dựng chung là gì?

Trả lời

6

Dưới đây là một cách có thể, được điều chỉnh từ lập trình hàm. Giả sử chúng ta có loại Stream có trạng thái nội tại, liên tục sinh ra các phần tử mới cho đến khi nó trả về null. Những người gọi bên ngoài không quan tâm loại trạng thái nội bộ của loại luồng là gì, vì vậy bạn có thể nhận được thông tin như

class Stream<E> { 
    <S> Stream(S initialState, StepFunction<E, S> stepFun) { 
    ... 
    } 
} 

mà không cần phải biết loại trạng thái nội bộ là gì.

+0

Aha, rất thú vị! +1 –

+0

+1, đây là một ví dụ tuyệt vời, mặc dù nó cheats một chút vì OP đặc biệt nói rằng loại kèm theo không phải là chung chung. ('Stream ' sẽ là chung chung.) –

+0

@JohnFeminella - Tôi chỉ có nghĩa là ví dụ cụ thể của tôi 'Foo' không phải là chung chung, chỉ để tránh nhầm lẫn. Tôi không có nghĩa đó là một hạn chế về câu trả lời. –

2

Một trường hợp sử dụng mà tôi có thể nghĩ đến là khi bạn muốn hạn chế đối số hàm tạo cho nhiều loại. Chỉ có cú pháp chung cho phép bạn khai báo một constructor lấy List của Number s mà cũng thực hiện RandomAccess:

public <L extends List<? extends Number> & RandomAccess> Foo(L raNumList) { } 

... 

Foo f1 = new Foo(new ArrayList<Integer>()); 
Foo f2 = new Foo(new LinkedList<Integer>()); //compiler error 
+1

Hoặc để tương thích ngược với mã tiền chung chung, trong đó 'Foo' lấy một' Object', nhưng bây giờ bạn muốn một cái gì đó chuyên sâu hơn một chút. '> Foo (T obj)'./Không thực sự thuyết phục bất kỳ điều này là ở tất cả các phổ biến. –

5

Một điều tôi có thể nghĩ ra khỏi đỉnh đầu của tôi là bạn có thể đảm bảo rằng giới hạn được đáp ứng trong theo cùng một cách trên nhiều thông số.

Tham dự một constructor rõ ràng là ngu ngốc và giả tạo nhưng có giá trị mà bản danh sách từ một nguồn tới một mục tiêu:

public <T> Foo (List<T> listA, List<T> listB) { 
    listA.addAll(listB); 
} 

Sử dụng ký tự đại diện ở đây một cách nhanh chóng sẽ trở thành khá khó chịu và có lẽ không làm những gì bạn muốn anyway. Nó cũng sẽ là một hạn chế hoàn toàn tùy ý để không cho phép nó. Vì vậy, nó có ý nghĩa với tôi rằng các spec ngôn ngữ cho phép nó.

+2

Hoặc sử dụng 'T' trong nội dung:' Foo (Danh sách ts) {T t0 = ts.get (0); ts.set (0, ts.get (1)); ts.set (1, t0); } '. Được rồi, không phải là một ví dụ thuyết phục. –

1

Bạn có thể thực thi một số ràng buộc nhất định đối với các tham số hàm tạo. Ví dụ. đoạn mã sau yêu cầu hai tham số thực hiện giao diện InterfaceA và InterfaceB.

<T extends InterfaceA & InterfaceB > Foo(T t1, T t2) { 

} 
1

Sử dụng chính là đảm bảo rằng các ràng buộc loại được đáp ứng giữa nhiều thông số. Dưới đây là một ví dụ mà đặt một loạt các thành phần trên một dây chuyền lắp ráp theo thứ tự đúng:

public <T> AssemblyLine(T[] starting, List<T> components) { 
    T[] a = components.toArray(starting); 
    Arrays.sort(a); 
    this.conveyorBelt.add(a); 
} 

Ở đây <T> đảm bảo rằng T[]List<T> giữ cùng loại T, và không (nói), Integer[]List<string>.

+0

+1 Tôi quên về mảng chung và cần phải vượt qua một ví dụ, vv - tốt đẹp nhất (tôi nghĩ rằng có một lỗi đánh máy mặc dù - nó phải là 'components.toArray') –

+0

Cảm ơn! Tôi sửa nó lên. –

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