2013-01-09 35 views
136

Gần đây tôi đã xem qua chú thích java @SafeVarargs. Googling cho những gì làm cho một chức năng variadic trong Java không an toàn để lại cho tôi khá bối rối (? Ngộ độc đống xóa loại?), Vì vậy tôi muốn biết một vài điều:Chú thích Java SafeVarargs, thực tiễn tiêu chuẩn hay tốt nhất có tồn tại không?

  1. Điều gì làm cho một chức năng Java variadic không an toàn trong @SafeVarargs cảm giác (tốt nhất là được giải thích dưới dạng một ví dụ chuyên sâu)?

  2. Tại sao chú thích này lại theo ý của người lập trình? Đây không phải là một cái gì đó trình biên dịch sẽ có thể kiểm tra?

  3. Có một số tiêu chuẩn nào phải tuân theo để đảm bảo chức năng của mình thực sự là an toàn không? Nếu không, các phương pháp hay nhất để đảm bảo điều đó là gì?

+1

Bạn đã xem ví dụ (và giải thích) trong [JavaDoc] (http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html) chưa? – jlordo

+2

Đối với câu hỏi thứ ba của bạn, một thực hành là luôn luôn có một yếu tố đầu tiên và những người khác: 'retType myMethod (Arg đầu tiên, Arg ... những người khác)'. Nếu bạn không chỉ định 'first', một mảng trống được cho phép và bạn có thể có một phương thức có cùng tên với cùng kiểu trả về không tham số, có nghĩa là JVM sẽ có một thời gian khó xác định phương thức nào gọi là. – fge

+4

@jlordo Tôi đã làm, nhưng tôi không hiểu tại sao nó được đưa ra trong ngữ cảnh varargs vì người ta có thể dễ dàng thay thế tình trạng này bên ngoài một hàm varargs (đã xác minh điều này, biên dịch với cảnh báo an toàn loại và các lỗi trên thời gian chạy) .. – Oren

Trả lời

189

1) Có nhiều ví dụ trên Internet và trên StackOverflow về vấn đề cụ thể với generics và varargs. Về cơ bản, đó là khi bạn có một số biến của tham số của một loại kiểu tham số:

void foo(T... args); 

Trong Java, varargs là một đường cú pháp mà phải trải qua một đơn giản "lại viết" tại thời gian biên dịch: một tham số varargs loại X... được chuyển đổi thành thông số loại X[]; và mỗi khi một cuộc gọi được thực hiện cho phương pháp varargs này, trình biên dịch thu thập tất cả các "đối số biến" đi vào tham số varargs và tạo một mảng giống như new X[] { ...(arguments go here)... }.

Điều này hoạt động tốt khi loại chênh lệch bê tông như String.... Khi đó là biến kiểu như T..., nó cũng hoạt động khi T được biết là loại cụ thể cho cuộc gọi đó. ví dụ. nếu phương pháp trên là một phần của một lớp học Foo<T> và bạn có tham chiếu Foo<String>, thì gọi số foo trên đó sẽ không sao vì chúng tôi biết TString tại thời điểm đó trong mã.

Tuy nhiên, nó không hoạt động khi "giá trị" của T là một thông số loại khác. Trong Java, không thể tạo một mảng kiểu thành phần kiểu tham số (new T[] { ... }). Vì vậy, Java thay vì sử dụng new Object[] { ... } (ở đây Object là giới hạn trên của T; nếu có giới hạn trên là một cái gì đó khác nhau, nó sẽ là thay vì Object), và sau đó cung cấp cho bạn một cảnh báo trình biên dịch.

Vì vậy, điều gì sai khi tạo new Object[] thay vì new T[] hoặc bất kỳ điều gì? Vâng, các mảng trong Java biết kiểu thành phần của chúng khi chạy. Do đó, đối tượng mảng được truyền sẽ có kiểu thành phần sai khi chạy.

Đối lẽ việc sử dụng phổ biến nhất của varargs, chỉ đơn giản là để lặp qua các yếu tố, điều này là không có vấn đề (bạn không quan tâm về loại thời gian chạy của mảng), vì vậy đây là an toàn:

@SafeVarargs 
final void foo(T... args) { 
    for (T x : args) { 
     // do stuff with x 
    } 
} 

Tuy nhiên, đối với bất cứ điều gì phụ thuộc vào loại thành phần thời gian chạy của mảng được truyền, nó sẽ không an toàn.Dưới đây là một ví dụ đơn giản của một cái gì đó là không an toàn và bị treo:

class UnSafeVarargs 
{ 
    static <T> T[] asArray(T... args) { 
    return args; 
    } 

    static <T> T[] arrayOfTwo(T a, T b) { 
    return asArray(a, b); 
    } 

    public static void main(String[] args) { 
    String[] bar = arrayOfTwo("hi", "mom"); 
    } 
} 

Vấn đề ở đây là chúng ta phụ thuộc vào loại argsT[] để trở lại nó như T[]. Nhưng thực sự loại đối số trong thời gian chạy không phải là một thể hiện của T[].

3) Nếu phương pháp của bạn có một đối số kiểu T... (trong đó T là bất kỳ tham số type), sau đó:

  • an toàn: Nếu phương pháp của bạn chỉ phụ thuộc vào thực tế là các yếu tố của mảng là trường hợp của T
  • không an toàn: Nếu nó phụ thuộc vào thực tế là mảng là một thể hiện của T[]

những điều mà phụ thuộc vào loại thời gian chạy của mảng bao gồm: trở về nó như loại T[], đi qua nó như là một cuộc tranh cãi với một tham số kiểu T[], nhận kiểu mảng sử dụng .getClass(), đi qua nó để phương pháp mà phụ thuộc vào loại thời gian chạy của các mảng, như List.toArray()Arrays.copyOf() vv

2) Sự khác biệt Tôi đã đề cập ở trên là quá phức tạp để dễ dàng phân biệt tự động.

+4

well Bây giờ tất cả có ý nghĩa. cảm ơn. vì vậy chỉ để thấy rằng tôi hiểu bạn hoàn toàn, hãy nói rằng chúng ta có một lớp 'FOO 'sẽ an toàn để trả về T [] của một phương thức varargs như là một mảng' Somthing'? – Oren

+4

@Oren: nó phải là – newacct

+15

Có thể đáng lưu ý rằng một trường hợp mà nó được (có lẽ là luôn luôn chính xác để sử dụng '@ SafeVarargs' là khi điều duy nhất bạn làm với mảng là vượt qua nó đến một phương pháp đã được chú thích (ví dụ: tôi thường thấy mình viết các phương thức vararg tồn tại để chuyển đổi đối số của chúng thành một danh sách bằng cách sử dụng 'Arrays.asList (...)' và chuyển nó sang phương thức khác, trường hợp này luôn có thể được chú thích bằng '@ SafeVarargs' vì 'Arrays.asList' có chú thích). – Jules

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