Instansiasi objek dari parameter tipe


90

Saya sudah mendapat kelas template sebagai berikut:

class MyClass<T>
{
    T field;
    public void myMethod()
    {
       field = new T(); // gives compiler error
    }
}

Bagaimana cara membuat instance baru T di kelas saya?

Jawaban:


86

Setelah penghapusan jenis, semua yang diketahui tentang Titu adalah beberapa subkelas Object. Anda perlu menentukan beberapa pabrik untuk membuat instance T.

Satu pendekatan bisa menggunakan Supplier<T>:

class MyClass<T> {

  private final Supplier<? extends T> ctor;

  private T field;

  MyClass(Supplier<? extends T> ctor) {
    this.ctor = Objects.requireNonNull(ctor);
  }

  public void myMethod() {
    field = ctor.get();
  }

}

Penggunaannya mungkin terlihat seperti ini:

MyClass<StringBuilder> it = new MyClass<>(StringBuilder::new);

Alternatifnya, Anda dapat memberikan Class<T>objek, lalu menggunakan refleksi.

class MyClass<T> {

  private final Constructor<? extends T> ctor;

  private T field;

  MyClass(Class<? extends T> impl) throws NoSuchMethodException {
    this.ctor = impl.getConstructor();
  }

  public void myMethod() throws Exception {
    field = ctor.newInstance();
  }

}

Di paket apa Supplierlokasinya? `MyClass (Class <? Extends T> impl)` harus mendeklarasikan `throws NoSuchMethodException` untuk dikompilasi. Jawaban Anda sayangnya tidak ramah untuk pemula Java.
purucat

@ user927387java.util.function.Supplier
erickson

Pemasok <T> membutuhkan Java 8, JFTR untuk dimanapun layak.
Fran Marzoa

14

Pendekatan non-reflektif lainnya adalah dengan menggunakan pola Builder / Pabrik Abstrak hybrid.

Di Java Efektif, Joshua Bloch membahas pola Builder secara detail, dan menganjurkan antarmuka Builder umum:

public interface Builder<T> {
  public T build();
}

Pembuat beton dapat mengimplementasikan antarmuka ini, dan kelas luar dapat menggunakan pembuat beton untuk mengonfigurasi Builder sesuai kebutuhan. Pembangun dapat diteruskan ke MyClass sebagai Builder<T>.

Dengan menggunakan pola ini, Anda bisa mendapatkan instance baru T, meskipun Tmemiliki parameter konstruktor atau memerlukan konfigurasi tambahan. Tentu saja, Anda memerlukan beberapa cara untuk meneruskan Builder ke MyClass. Jika Anda tidak dapat mengirimkan apa pun ke MyClass, maka Builder dan Pabrik Abstrak keluar.


12

Ini mungkin lebih berat daripada yang Anda cari, tetapi juga akan berhasil. Perhatikan bahwa jika Anda menggunakan pendekatan ini, akan lebih masuk akal untuk memasukkan pabrik ke dalam MyClass saat dibuat daripada meneruskannya ke metode Anda setiap kali dipanggil.

interface MyFactory<T> 
{
    T newObject();
}

class MyClass<T> 
{
    T field;
    public void myMethod(MyFactory<T> factory)
    {
       field = factory.newObject()
    }
}

1
Baik, pendekatan non-reflektif; refleksi tidak selalu menjadi pilihan. myMethod seharusnya dapat menerima MyFactory <? meluas T>, kan?
erickson

1
Panggilan bagus - Anda ingin meletakkan wildcard terbatas di pabrik untuk mengizinkan objek tipe T dan subkelas T dibuat di myMethod ().
Dan Hodge


0

Salah satu opsinya adalah menggunakan Object

{field = (T) new Object();}

field awalnya akan menjadi tipe Object, tapi kemudian akan diturunkan menjadi tipe T. Ini adalah salah satu yang jelek karena mengurangi casting ke nol adalah tujuan yang seharusnya untuk inisialisasi objek. Tapi saya pikir ini akan berhasil.


-2

Kelas classOfT

        try {
            t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
        } catch (Exception e) {
            e.printStackTrace();
        }

2
Di mana kelas classOfT itu dideklarasikan?
Fran Marzoa
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.