Mục đích là cho bất kỳ lớp X
kéo dài TaskCollection
, khi một hoạt động groupby là được thực hiện, bộ sưu tập được sử dụng cho các giá trị bản đồ cũng là các thể hiện của lớp X
.
Trong trường hợp đó, gần nhất bạn có thể tới đó là một cái gì đó như sau:
class Task {}
class Assertion extends Task {}
abstract class TaskCollection<E extends Task, C extends TaskCollection<E, C>> extends HashSet<E> {
<K> Map<K, C> groupBy(Function<E, K> groupingFunction) {
return this.stream()
.collect(Collectors.groupingBy(
groupingFunction,
Collectors.toCollection(this.collectionSupplier())
));
}
protected abstract Supplier<C> collectionSupplier();
}
class AssertionCollection extends TaskCollection<Assertion, AssertionCollection> {
@Override
protected Supplier<AssertionCollection> collectionSupplier() {
return AssertionCollection::new;
}
}
Chú ý rằng định nghĩa của TaskCollection
trên không khá ngăn chặn các lớp con của việc sử dụng lớp khác TaskCollection
cho các giá trị bản đồ groupby của họ . Ví dụ này cũng sẽ biên dịch:
class AssertionCollectionOther extends TaskCollection<Assertion, AssertionCollectionOther> {...}
class AssertionCollection extends TaskCollection<Assertion, AssertionCollectionOther> {...}
Đáng tiếc là nó không thể áp đặt một hạn chế như vậy, ít nhất là cho bây giờ, khi bạn không thể làm tài liệu tham khảo cho các lớp đang được khai báo trong C type-tham số ký tự đại diện.
Nếu bạn có thể giả định rằng con cháu có một hàm tạo miễn phí là nhà cung cấp bộ sưu tập, bạn có thể cung cấp triển khai mặc định cho collectionSupplier
. Giá bạn trả là cần phải tắt tiếng cảnh báo "không được kiểm tra" (không phải là vấn đề thực) và không tuân thủ các lớp (không cung cấp hàm tạo tham số) sẽ không bị lỗi khi biên dịch nhưng thời gian chạy ít lý tưởng :
import java.util.function.*;
import java.util.*;
import java.util.stream.*;
class Task {}
class Assertion extends Task {}
class TaskCollection<E extends Task, C extends TaskCollection<E, C>> extends HashSet<E> {
<K> Map<K, C> groupBy(Function<E, K> groupingFunction) {
return this.stream()
.collect(Collectors.groupingBy(
groupingFunction,
Collectors.toCollection(this.collectionSupplier())
));
}
@SuppressWarnings("unchecked")
protected Supplier<C> collectionSupplier() {
return() -> {
try {
return (C) this.getClass().newInstance();
} catch (Exception ex) {
throw new RuntimeException(String.format("class %s is not a proper TaskCollection", this.getClass()), ex);
}
};
}
}
class AssertionCollection extends TaskCollection<Assertion, AssertionCollection> {
// This override is not needed any longer although still could
// be included in order to produce a slightly faster
// customized implementation:
//@Override
//protected Supplier<AssertionCollection> collectionSupplier() {
// return AssertionCollection::new;
//}
}
Nếu bạn khai báo collectionSupplier
như final
bạn sẽ có hiệu quả buộc các lớp con để luôn luôn trở lại trường hợp của lớp của mình với sự báo trước rằng một, sau đó không có ý nghĩa, tuyên bố như class AssertionCollection extends TaskCollection<Assertion, AssertionCollectionOther>
sẽ vẫn biên dịch và sản xuất thời gian chạy đúc ngoại lệ xuống đường.
Nhận xét về việc triển khai của bạn: có vẻ như 'TaskCollection' có một' HashSet' có ý nghĩa tốt hơn 'TaskCollection' là một' HashSet'. Việc sử dụng 'HashSet' giống như một chi tiết thực hiện. Bạn có cần kế thừa từ 'HashSet' không? – scottb