Làm thế nào để tạo một phương thức Java Generic tĩnh?


172

Sau đây là đoạn trích về cách tạo một lớp chung java để nối thêm một mục vào một mảng. Làm thế nào tôi có thể biến appendToArray thành một phương thức tĩnh. Thêm tĩnh vào chữ ký phương thức dẫn đến lỗi biên dịch.

public class ArrayUtils<E> {

        public E[] appendToArray(E[] array, E item) {
            E[] result = (E[])new Object[array.length+1];
            result[array.length] = item;
            return result;
        }
}

Những lỗi biên dịch nào bạn nhận được? Ngoài ra, tại sao không chỉ sử dụng một trong các thùng chứa thư viện tiêu chuẩn?
Karl Knechtel

1
Lỗi biên dịch: Tôi thực sự đã thêm bộ sửa đổi tĩnh không chính xác .. Sử dụng Bộ sưu tập: Có sử dụng bộ sưu tập sẽ là lý tưởng nhưng câu hỏi không phải là về bộ sưu tập so với mảng, trường hợp sử dụng của tôi yêu cầu một mảng.
Chris Johnson

Lưu ý bạn sẽ cần sử dụng phản xạ (EVIL) để ngăn chặn mã máy khách ném ngoại lệ trong một số trường hợp nhưng không phải tất cả các trường hợp (tốt đẹp). Tốt nhất là tránh các mảng tham chiếu.
Tom Hawtin - tackline

Câu trả lời:


283

điều duy nhất bạn có thể làm là thay đổi chữ ký của mình thành

public static <E> E[] appendToArray(E[] array, E item)

Chi tiết quan trọng:

Các biểu thức chung trước giá trị trả về luôn giới thiệu (khai báo) một biến kiểu chung mới.

Ngoài ra, các biến loại giữa các loại ( ArrayUtils) và phương thức tĩnh ( appendToArray) không bao giờ can thiệp lẫn nhau.

Vì vậy, điều này có nghĩa là gì: Trong câu trả lời của tôi <E>sẽ ẩn Etừ ArrayUtils<E>nếu phương thức sẽ không static. VÀ <E>không có gì để làm với Etừ ArrayUtils<E>.

Để phản ánh thực tế này tốt hơn, một câu trả lời đúng hơn sẽ là:

public static <I> I[] appendToArray(I[] array, I item)

30
Cũng xin lưu ý rằng hoàn toàn không có mối quan hệ nào giữa biến loại cấp độ lớp Evà biến loại phương thức tĩnh E. Tôi coi đó là cách thực hành tốt hơn nhiều để sử dụng một tên biến khác nhau khi khai báo các phương thức chung, tĩnh hoặc mặt khác, bên trong các lớp chung.
Thẩm phán tâm thần

nhưng trong trường hợp này tôi có thể truyền một đối tượng thuộc các loại khác nhau vào các thông số. Giống như tôi có thể vượt qua mảng Integer [] làm mục param và Double đầu tiên.
Pinkpanther

Pinkpanther: Đúng, nhưng nó không gây hại gì, bởi vì phương thức tĩnh chỉ hoạt động trên một đối tượng mảng được truyền cho nó thông qua một tham số, vì vậy các phần tử của nó chắc chắn có đúng loại.
Dabbler

80
public static <E> E[] appendToArray(E[] array, E item) { ...

Lưu ý <E>.

Các phương thức chung tĩnh cần khai báo chung riêng ( public static <E>) tách biệt với khai báo chung chung của lớp ( public class ArrayUtils<E>).

Nếu trình biên dịch phàn nàn về sự không rõ ràng của kiểu trong việc gọi một phương thức chung tĩnh (một lần nữa không có khả năng trong trường hợp của bạn, nhưng nói chung, chỉ trong trường hợp), thì đây là cách gọi một phương thức chung tĩnh bằng cách sử dụng một kiểu cụ thể ( _class_.<_generictypeparams_>_methodname_):

String[] newStrings = ArrayUtils.<String>appendToArray(strings, "another string");

Điều này chỉ xảy ra nếu trình biên dịch không thể xác định loại chung vì, ví dụ như loại chung không liên quan đến các đối số phương thức.


10

Bạn cần di chuyển tham số loại đến mức phương thức để chỉ ra rằng bạn có một phương thức chung chứ không phải lớp chung:

public class ArrayUtils {
    public static <T> E[] appendToArray(E[] array, E item) {
        E[] result = (E[])new Object[array.length+1];
        result[array.length] = item;
        return result;
    }
}

1
Điều này sẽ không hoạt động vì bạn chưa xác định loại chung E. Trong trường hợp này, bạn vẫn phải có loại chung <E> trong định nghĩa lớp.
George Xavier

0

Tôi sẽ giải thích nó một cách đơn giản.

Các tướng được định nghĩa ở cấp Lớp hoàn toàn tách biệt với các tổng quát được định nghĩa ở cấp phương thức (tĩnh).

class Greet<T> {

    public static <T> void sayHello(T obj) {
        System.out.println("Hello " + obj);
    }
}

Khi bạn thấy mã ở trên ở bất cứ đâu, xin lưu ý rằng T được xác định ở cấp lớp không liên quan gì đến T được xác định trong phương thức tĩnh. Đoạn mã sau cũng hoàn toàn hợp lệ và tương đương với đoạn mã trên.

class Greet<T> {

    public static <E> void sayHello(E obj) {
        System.out.println("Hello " + obj);
    }
}

Tại sao phương thức tĩnh cần có các tổng quát riêng của nó tách biệt với các tổng quát của Lớp?

Điều này là do, phương thức tĩnh có thể được gọi mà không cần khởi tạo Class. Vì vậy, nếu Lớp chưa được khởi tạo, chúng ta chưa biết T. là gì, đây là lý do tại sao các phương thức tĩnh cần phải có tổng quát riêng.

Vì vậy, bất cứ khi nào bạn đang gọi phương thức tĩnh,

Greet.sayHello("Bob");
Greet.sayHello(123);

JVM diễn giải nó như sau.

Greet.<String>sayHello("Bob");
Greet.<Integer>sayHello(123);

Cả hai đều cho cùng một đầu ra.

Hello Bob
Hello 123
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.