2009-03-25 33 views
29

Tôi có một bộ các lớp chung chung khá phức tạp trong Java. Ví dụ, tôi có một giao diệnGõ bí danh cho các Generics Java

interface Doable<X,Y> { 
    X doIt(Y y); 
} 

và thực hiện

class DoableImpl implements Doable<Foo<Bar<Baz,Qux>>,Foo<Bar<Zot,Qux>>> { 
    Foo<Bar<Baz,Qux>> doIt(Foo<Bar<Zot,Qux>> fooBZQ) { ... } 
} 

Trong việc thực hiện thực tế, Doable có khá một vài phương pháp và vì vậy Foo<Bar<Baz,Qux>> vv, xuất hiện hơn và hơn nữa. (Tin tưởng rằng nó hay không, các loại chung chung là khá một chút đau đớn hơn này. Tôi đã đơn giản hóa chúng cho ví dụ này.)

Tôi muốn đơn giản hóa chúng, để tiết kiệm bản thân mình đánh máy và để giảm bớt căng thẳng trên mắt tôi. Điều tôi muốn là có một "bí danh loại" đơn giản cho Foo<Bar<Baz,Qux>>, v.v., nói FooBBQFooBZQ.

ý tưởng hiện tại của tôi là xác định các lớp wrapper:

class FooBBQ { 
    public static FooBBQ valueOf(Foo<Bar<Baz,Qux>> fooBBQ) { 
    return new FooBBQ(fooBBQ); 
    } 
    private Foo<Bar<Baz,Qux>> fooBBQ; 
    private FooBBQ(Foo<Bar<Baz,Qux>> fooBBQ) { 
    this.fooBBQ = fooBBQ; 
    } 
    public Foo<Bar<Baz,Qux>> toGeneric() { 
    return fooBBQ; 
    } 
} 

class FooBZQ { /* pretty much the same... */ } 

class DoableImpl implements Doable<FooBBQ,FooBZQ> { 
    FooBBQ doIt(FooBZQ fooBZQ) { ... } 
} 

này hoạt động tốt, nhưng nó có một vài hạn chế:

  1. Chúng ta cần phải xác định giấy gói riêng biệt cho mỗi trường hợp tổng quát. Các lớp bao bọc là ngắn và cách điệu, nhưng tôi không thể tìm ra cách để đóng băng chúng.
  2. Chúng tôi có phí dịch thuật (khái niệm, nếu không hoạt động) gọi valueOftoGeneric để chuyển đổi giữa FooBBQFoo<Bar<Baz,Qux>>. Ví dụ, nếu doIt cuộc gọi vào một số thói quen thư viện mà hy vọng một Foo<Bar<Zot,Qux>> (mà việc thực hiện thực hiện), chúng tôi kết thúc với một cái gì đó giống như

    return FooBBQ.valueOf(libraryCall(fooBZQ.toGeneric())) 
    

    nơi chúng tôi sẽ ban đã

    return libraryCall(fooBZQ); 
    

Có cách nào khác để có được hành vi "loại bí danh" mà tôi muốn ở đây không? Có lẽ sử dụng một số bộ công cụ macro của bên thứ ba? Hoặc tôi có cần phải chấp nhận rằng tôi sẽ phải làm rất nhiều cách gõ, một cách (sử dụng các loại chung trong việc thực hiện) hay cách khác (viết trình bao bọc cho chúng)? Có lẽ có nhiều thông số chung chung bay quanh đây chỉ là một ý tưởng tồi và tôi cần phải nghĩ lại vấn đề?

[CẬP NHẬT] OK, tôi đang cấm thêm bất kỳ câu trả lời "không làm điều đó". Lấy nó như là một cho rằng Foo<Bar<Baz,Qux>> có giá trị chính hãng trong lĩnh vực vấn đề của tôi (Pete Kirkham có thể đúng rằng nó có đủ giá trị để có được một lớp wrapper thích hợp với một tên mô tả). Nhưng đây là một vấn đề lập trình; không cố gắng xác định vấn đề.

+0

Tôi sẽ nói 'có thể' đối với người cuối cùng, nhưng không thực sự có bất kỳ phản hồi mang tính xây dựng nào: P Nó trông giống như một mớ hỗn độn! Chúc may mắn! –

+6

Tôi nghĩ câu hỏi này sẽ đưa ra một đề xuất tốt cho Java. Nếu bạn có thể đề xuất tính năng Loại Bí danh cho Oracle, tôi nghĩ đó sẽ là một tính năng ngôn ngữ tuyệt vời sẽ đơn giản hóa ngôn ngữ và làm cho mã nguồn dễ đọc hơn. –

+1

Đầu gối sâu trong Java ngày hôm nay và muốn làm sạch một số mã tiết ... không chỉ làm tôi không nhận được suy luận kiểu, nhưng không có loại bí danh hoặc. * sob * –

Trả lời

2

Có thể có nhiều thông số chung chung này bay xung quanh chỉ là ý tưởng tồi và tôi cần phải nghĩ lại vấn đề?

Rất có thể. Bạn có cần chuyên 'Doit' trong 8 chiều không?

Trong nhiều trường hợp, các loại này không tồn tại trong chân không và bạn nên suy nghĩ xem đối tượng miền nào 'trình bao bọc' của bạn đại diện thay vì sử dụng chúng như một tiện ích mã hóa.

0

Tôi sẽ nói rằng, có, bạn cần phải suy nghĩ lại vấn đề. Tuyên bố

class DoableImpl implements Doable<Foo<Bar<Baz,Qux>>,Foo<Bar<Zot,Qux>>> { 
    Foo<Bar<Baz,Qux>> doIt(Foo<Bar<Zot,Qux>> fooBZQ) { ... } 
} 

là trường hợp khá rõ ràng về việc sử dụng quá nhiều generics.

+0

Tôi không đồng ý. Nó có thể không được sử dụng quá mức của generics ... nó có thể chỉ là một sử dụng không rõ ràng của Generics. Nếu nó cung cấp an toàn loại bổ sung, thì nó có thể đáng để giảm đau, nhưng cũng phải có cách để làm điều này rõ ràng hơn. – Eddie

+0

Bạn có tin tôi nếu tôi nói nó thực sự khá rõ ràng (chỉ cực kỳ tiết) trong mã thực? Rõ ràng là không có điểm nào trong việc tham số hóa các đối tượng của bạn bởi Foo, Bar, và Qux ... –

+0

Tôi đã thấy (và làm) tồi tệ hơn, có những lúc nó có ý nghĩa ... nhưng chúng có thể khó theo. Tuy nhiên, các loại an toàn có thể được giá trị nó. – TofuBeer

9

Nếu bạn muốn an toàn loại đầy đủ, tôi không nghĩ rằng bạn có thể làm tốt hơn mà không có một số loại lớp bao bọc. Nhưng, tại sao không làm cho những lớp kế thừa/thực hiện các phiên bản gốc chung, như thế này:

public class FooBBQ extends Foo<Bar<Baz,Qux>> { 
... 
} 

này giúp loại bỏ sự cần thiết của toGeneric() phương pháp, và nó là rõ ràng hơn, theo ý kiến ​​của tôi, rằng nó chỉ là một loại bí danh . Ngoài ra, loại chung có thể được đúc thành FooBBQ mà không có cảnh báo trình biên dịch. Nó sẽ là sở thích cá nhân của tôi để làm cho giao diện Foo, Bar, Baz..., nếu có thể, ngay cả khi một số mã trùng lặp sẽ xảy ra trong quá trình triển khai.

Bây giờ, mà không biết miền vấn đề cụ thể, rất khó để nói liệu bạn cần, nói FooBBQ, như trong ví dụ của bạn, hoặc có lẽ một:

public class FooBar<X, Y> extends Foo<Bar<X, Y>> { 
... 
} 

Mặt khác, bạn có nghĩ về đơn giản cấu hình trình biên dịch Java không hiển thị một số cảnh báo chung và chỉ bỏ qua các phần của định nghĩa chung? Hoặc, sử dụng chiến lược đặt @SuppressWarnings("unchecked")? Nói cách khác, bạn có thể đặt DoableImpl chỉ "một phần được tổng quát hóa":

class DoableImpl implements Doable<Foo<Bar>>,Foo<Bar>> { 
    Foo<Bar> doIt(Foo<Bar> foobar) { ... } 
} 

và bỏ qua cảnh báo vì ít lộn xộn hơn? Một lần nữa, khó có thể quyết định mà không có một ví dụ cụ thể, nhưng đó là một điều bạn có thể thử.

+0

Mở rộng Foo > giúp bạn dịch miễn phí một chiều (tức là, ẩn hoàn toàn) với chi phí phải viết các nhà xây dựng phức tạp hơn (cho việc truyền xuống rõ ràng). Nó không rõ ràng với tôi rằng đó là một chiến thắng. Tôi muốn tránh các chú thích @SuppressWarnings càng nhiều càng tốt. –

5

Scala có hỗ trợ tốt cho loại bí danh. Ví dụ:

type FooBBQ = Foo[Bar[Baz,Qux]] 

Tôi nhận thấy câu trả lời này sẽ không hữu ích nếu bạn không có tùy chọn chuyển sang Scala. Nhưng nếu bạn có tùy chọn chuyển đổi, bạn có thể có thời gian dễ dàng hơn.

1

Vâng, Java không có bí danh loại để bạn không may mắn. Tuy nhiên, gõ bí danh đôi khi có thể được thay thế bằng các biến kiểu! Vì vậy, chúng tôi giải quyết vấn đề của quá nhiều generics với nhiều generics hơn nữa!

Như bạn đã bị tước tất cả nội dung từ ví dụ của bạn tôi không thể đoán được nơi hay nó có ý nghĩa để giới thiệu các biến kiểu bổ sung, nhưng đây là một trong những phân hủy có thể:

class DoableImplFoo<A,B> implements Doable<Foo<A>,Foo<B>> { 
    public DoableImplFoo(SomePropertyOf<A,B> aAndBAreGoodEnough) { ... } 
    Foo<A> doIt(Foo<B> fooB) { ... } 
} 

Khi bạn nhanh chóng A để Bar<Baz,Qux>B đến Bar<Zot,Qux> sau đó bạn có thể thấy rằng có một số bản mẫu lại, nhưng nó có thể ít hơn so với những gì ban đầu bạn có.