2017-03-26 19 views
9

Tôi đã lớp bộ sưu tập sau đây có chứa một phương pháp để nhóm các yếu tố trong một bản đồ, nơi mỗi giá trị có kiểu của lớp cách gọi nóJava Collection với phương pháp chung chung và lớp con

class TaskCollection<E extends Task> extends HashSet<E> { 
    <K> Map<K, ? extends TaskCollection<E>> groupBy(Function<E, K> groupingFunction) { 
     return this.stream() 
      .collect(Collectors.groupingBy(
        groupingFunction, 
        Collectors.toCollection(this.collectionConstructor()) 
      )); 
    } 

    Supplier<? extends TaskCollection<E>> collectionConstructor() { 
     return TaskCollection::new; 
    } 
} 

Những gì tôi muốn là để có thể tạo các lớp con sử dụng phương thức groupBy trả về các giá trị mới của bản thân làm giá trị bản đồ.
Sau đây là một ví dụ

class AssertionCollection extends TaskCollection<Assertion> { 
    Map<Person, AssertionCollection> groupByPerson() { 
     return this.groupBy(Assertion::assignedPerson); 
    } 

    @Override 
    Supplier<AssertionCollection> collectionConstructor() { 
     return AssertionCollection::new; 
    } 
} 

Vấn đề là trong phương pháp groupByPerson. Trình biên dịch sẽ gửi một lỗi cho cuộc gọi groupBy.

Error:(15, 28) java: incompatible types: no instance(s) of type variable(s) K exist so that java.util.Map<K,? extends TaskCollection<Assertion>> conforms to java.util.Map<Person,AssertionCollection>

Tôi mới trong Java vì vậy tôi khá chắc chắn có điều gì đó ngu ngốc Tôi không thấy

+2

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

Trả lời

4

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.

+1

Đó là những gì tôi đang tìm kiếm! Cảm ơn bạn – Fed03

+1

Tôi đã xóa câu trả lời xấu xí của mình. câu trả lời hay và bỏ phiếu. –

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