Có thể giải quyết được một mảng chung của T được tạo cho cảnh báo trình biên dịch tham số varargs không?


153

Đây là một phiên bản đơn giản của mã đang được đề cập, một lớp chung sử dụng một lớp khác với các tham số kiểu chung và cần truyền một trong các kiểu chung cho một phương thức có tham số varargs:

class Assembler<X, Y> {
    void assemble(X container, Y... args) { ... }
}

class Component<T> {
    void useAssembler(T something) {

        Assembler<String, T> assembler = new Assembler<String, T>();

        //generates warning:
        // Type safety : A generic array of T is
        // created for a varargs parameter
        assembler.assemble("hello", something);
    }

}

Có cách nào chính xác để chuyển dọc theo tham số chung cho phương thức varargs mà không gặp phải cảnh báo này không?

Tất nhiên một cái gì đó như

assembler.assemble("hello", new T[] { something });

không hoạt động vì bạn không thể tạo mảng chung.


3
Một điều kỳ lạ. Có vẻ như trình biên dịch sẽ có thể đảm bảo an toàn loại đầy đủ ở đây.
erickson

3
Mục liên quan trong Câu hỏi thường gặp về Java Generics của Angelika Langer: angelikalanger.com/GenericsFAQ/FAQSections/ từ
Flow

Câu trả lời:


88

Ngoài việc thêm vào @SuppressWarnings("unchecked"), tôi không nghĩ vậy.

Đây báo cáo lỗi có thêm thông tin nhưng nó nắm để trình biên dịch không thích mảng của các kiểu generic.


3
Quên đề cập đến tôi muốn tránh @SuppressWarnings ("không được kiểm tra"). Báo cáo lỗi đó cho tôi ít hy vọng!
matt b

3
Như Joshua Bloch đưa nó vào Java hiệu quả: "Đừng trộn lẫn các tổng quát và mảng."
Timmos

20
sau đó, ngầm: không sử dụng Varargs với Generics! Phải ... quyết định ánh xạ varargs sang Array chứ không phải Collection sẽ tiếp tục châm ngòi java mãi mãi. Làm tốt lắm ông Gosling.
bernstein

57

Tom Hawtin đã chỉ ra điều này trong một bình luận, nhưng để rõ ràng hơn: có, bạn có thể giải quyết vấn đề này tại trang web khai báo (chứ không phải là các trang web gọi (có khả năng nhiều)): chuyển sang JDK7.

Như bạn có thể thấy trong bài đăng trên blog của Joseph Darcy, bài tập Project Coin để chọn một số cải tiến ngôn ngữ gia tăng nhỏ cho Java 7 đã chấp nhận đề xuất của Bob Lee để cho phép một cái gì đó giống như @SuppressWarnings("varargs")ở phương thức biến cảnh báo này biến mất trong tình huống mà nó được biết đến an toàn

Điều này đã được thực hiện trong OpenJDK với cam kết này .

Điều này có thể hữu ích hoặc không hữu ích cho dự án của bạn (nhiều người sẽ không vui khi chuyển sang phiên bản không ổn định trước khi phát hành của JVM!) Nhưng có lẽ nó - hoặc có lẽ ai đó đã tìm thấy câu hỏi này sau đó (sau khi JDK7 không hoạt động ) sẽ tìm thấy nó hữu ích.


7
Tính năng Project Coin được đề cập hiện có sẵn - xem @SafeVarargs trong Java 7.
George Hawkins

E thay thế trong đề xuất của Bob là hấp dẫn.
Christopher Perry

Java 8 dường như sử dụng @SafeVarargs thay vì @SuppressWarnings ("varargs")
Paul Wintz

17

Nếu bạn đang theo một giao diện thông thạo, bạn có thể thử mẫu xây dựng. Không ngắn gọn như varargs nhưng nó là loại an toàn.

Một phương pháp gõ tổng quát tĩnh có thể loại bỏ một số mẫu soạn sẵn khi sử dụng trình xây dựng, trong khi vẫn giữ được độ an toàn của loại.

Người xây dựng

public class ArgBuilder<T> implements Iterable<T> {

    private final List<T> args = new ArrayList<T>();

    public ArgBuilder<T> and(T arg) {
        args.add(arg);
        return this;
    }

    @Override
    public Iterator<T> iterator() {
        return args.iterator();
    }

    public static <T> ArgBuilder<T> with(T firstArgument) {
        return new ArgBuilder<T>().and(firstArgument);
    }
}

Sử dụng nó

import static com.example.ArgBuilder.*;

public class VarargsTest {

    public static void main(String[] args) {
        doSomething(new ArgBuilder<String>().and("foo").and("bar").and("baz"));
        // or
        doSomething(with("foo").and("bar").and("baz"));
    }

    static void doSomething(Iterable<String> args) {
        for (String arg : args) {
            System.out.println(arg);
        }
    }
}

1
Sức mạnh của thành phần. Tôi thích điều này hơn nhiều so với varargs, nó biểu cảm hơn.
Christopher Perry

1
@ChristopherPerry bạn cũng phải xem xét cơ sở mã của mình. Cơ sở Collection(trong trường hợp này là một ArrayList) được buộc theo người gọi, trong khi họ có thể biết rằng một LinkedListmảng phù hợp hơn hoặc chính một mảng bất biến (chẳng hạn như các biến thể từ câu hỏi OP). Trong trường hợp sử dụng không chuyên biệt, điều này có thể phù hợp, nhưng chỉ ra rằng đây cũng là một hạn chế, theo một cách nào đó, tùy thuộc vào mã xung quanh điều này và nhu cầu của bạn.
searchengine27

5

Hoàn toàn truyền tham số cho Object trong lời gọi phương thức vararg sẽ làm cho trình biên dịch hài lòng mà không cần dùng đến @SuppressWarnings.

public static <T> List<T> list( final T... items )
{
    return Arrays.asList( items );
}

// This will produce a warning.
list( "1", 2, new BigDecimal( "3.5" ) )

// This will not produce a warning.
list( (Object) "1", (Object) 2, (Object) new BigDecimal( "3.5" ) )

// This will not produce a warning either. Casting just the first parameter to 
// Object appears to be sufficient.
list( (Object) "1", 2, new BigDecimal( "3.5" ) )

Tôi tin rằng vấn đề ở đây là trình biên dịch cần phải tìm ra loại mảng cụ thể để tạo. Nếu phương thức không chung chung, trình biên dịch có thể sử dụng thông tin loại từ phương thức. Nếu phương thức này là chung, nó cố gắng tìm ra kiểu mảng dựa trên các tham số được sử dụng trong lệnh gọi. Nếu các loại tham số là homogen, nhiệm vụ đó là dễ dàng. Nếu chúng khác nhau, trình biên dịch cố gắng quá thông minh theo ý kiến ​​của tôi và tạo ra một mảng chung kiểu kết hợp. Sau đó, nó cảm thấy buộc phải cảnh báo bạn về nó. Một giải pháp đơn giản hơn là tạo Object [] khi loại không thể thu hẹp tốt hơn. Các giải pháp trên chỉ có thế.

Để hiểu rõ hơn về điều này, hãy chơi xung quanh với các yêu cầu cho phương thức danh sách trên so với phương thức list2 sau đây.

public static List<Object> list2( final Object... items )
{
    return Arrays.asList( items );
}

Điều này cũng hoạt động, ví dụ: Iterator <?> It = Arrays.asList ((Object) t) .iterator; if (if, hasNext ()) {class = it.next (). getClass (); } chẳng hạn để lấy lớp của một đối tượng ra khỏi một mảng không xác định.
ggb667

2

Bạn có thể thêm @SafeVarargs vào phương thức kể từ Java 7 và bạn không phải chú thích vào mã máy khách.

class Assembler<X, Y> {

    @SafeVarargs
    final void assemble(X container, Y... args) {
        //has to be final...
    }
}

1

Bạn có thể có quá tải các phương thức. Điều này không giải quyết được vấn đề của bạn nhưng nó giảm thiểu số lượng cảnh báo (và vâng, đó là một vụ hack!)

class Assembler<X, Y> {
  void assemble(X container, Y a1) { ... }
  void assemble(X container, Y a1, Y a2) { ... }
  void assemble(X container, Y a1, Y a2, Y a3) { ... }
  void assemble(X container, Y a1, Y a2, Y a3, Y a4) { ... }
  void assemble(X container, Y... args) { ... }
}

23
Ew. Đây chính xác là loại hack mà varargs được cho là ngăn chặn.
Amanda S

1
Đây có thể là một cách tiếp cận hợp lệ, ví dụ, hãy xem ImmutableSet.of của Guava .
Jonathan

1

Đây là một vấn đề rất dễ giải quyết: Sử dụng List<T>!

Mảng loại tham chiếu nên tránh.

Trong phiên bản hiện tại của Java (1.7), bạn có thể đánh dấu phương thức @SafeVargssẽ loại bỏ cảnh báo khỏi người gọi. Mặc dù cẩn thận với điều đó, và bạn vẫn tốt hơn nếu không có mảng kế thừa.

Xem thêm Các cảnh báo và lỗi trình biên dịch được cải tiến khi sử dụng các tham số chính thức không thể xác định lại với ghi chú công nghệ của phương thức Varargs .


6
Điều này là không thể tránh khỏi với một tham số varargs, phải không?
matt b

4
Có một đề xuất cho JDK7 là cho phép triệt tiêu cảnh báo đi vào khai báo phương thức varargs thay vì sử dụng nó.
Tom Hawtin - tackline

11
Điều này hoàn toàn bỏ qua một khía cạnh quan trọng của câu hỏi của tác giả - các tham số varargs tạo ra một mảng và tạo ra cảnh báo này.
Daniel Yankowsky

2
Tôi đồng ý với @Tom Hawtin - tackline. Để biết chi tiết, hãy xem Bloch << Effecive Java >> Mục 25: Thích danh sách cho mảng.
Stan Kurilin

2
Tôi thường đồng ý với Bloch về điều này, nhưng varargs là một ngoại lệ rõ ràng với quy tắc ...
Joeri Hendrickx

0

Khi làm việc với các mảng thuộc loại chung, tôi buộc phải chuyển một tham chiếu đến loại chung. Với điều đó, tôi thực sự có thể thực hiện mã chung, sử dụng java.lang.reflect.Array.

http://java.sun.com/javase/6/docs/api/java/lang/reflect/Array.html


Tôi không làm việc với các mảng kiểu chung, mặc dù không trực tiếp, chỉ là các biến thể của kiểu chung.
matt b
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.