อินสแตนซ์ออบเจ็กต์ของพารามิเตอร์ประเภท


90

ฉันมีคลาสเทมเพลตดังนี้:

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

ฉันจะสร้างอินสแตนซ์ใหม่ของ T ในชั้นเรียนได้อย่างไร

คำตอบ:


86

หลังจากการลบประเภทสิ่งที่รู้Tก็คือมันเป็นคลาสย่อยของObject. Tคุณต้องระบุโรงงานบางส่วนในการสร้างอินสแตนซ์

แนวทางหนึ่งสามารถใช้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();
  }

}

การใช้งานอาจมีลักษณะดังนี้:

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

หรือคุณสามารถระบุClass<T>วัตถุจากนั้นใช้การสะท้อนกลับ

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();
  }

}

แพ็คเกจอะไรSupplierอยู่ใน? `MyClass (Class <? expands T> im)` ต้องประกาศว่าจะคอมไพล์ NoSuchMethodException คำตอบของคุณไม่เหมาะสำหรับผู้เริ่มต้น Java
purucat

@ user927387java.util.function.Supplier
erickson

ซัพพลายเออร์ <T> ต้องการ Java 8, JFTR ทุกที่ที่คุ้มค่า
Fran Marzoa

14

อีกวิธีหนึ่งที่ไม่สะท้อนแสงคือการใช้รูปแบบ Builder / Abstract Factory แบบผสม

ใน Java ที่มีประสิทธิภาพ Joshua Bloch จะอธิบายรายละเอียดเกี่ยวกับรูปแบบ Builder และสนับสนุนอินเทอร์เฟซ Builder ทั่วไป:

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

ผู้สร้างคอนกรีตสามารถใช้อินเทอร์เฟซนี้ได้และชั้นเรียนภายนอกสามารถใช้ตัวสร้างคอนกรีตเพื่อกำหนดค่าตัวสร้างได้ตามต้องการ ตัวสร้างสามารถส่งผ่านไปยัง MyClass เป็นไฟล์Builder<T>.

เมื่อใช้รูปแบบนี้คุณจะได้รับอินสแตนซ์ใหม่Tแม้ว่าจะTมีพารามิเตอร์ตัวสร้างหรือต้องการการกำหนดค่าเพิ่มเติมก็ตาม แน่นอนว่าคุณจะต้องมีวิธีส่ง Builder ไปยัง MyClass หากคุณไม่สามารถส่งผ่านอะไรเข้าไปใน MyClass ได้แสดงว่า Builder และ Abstract Factory จะหมดลง


12

สิ่งนี้อาจมีน้ำหนักมากกว่าที่คุณกำลังมองหา แต่ก็ใช้ได้ผลเช่นกัน โปรดทราบว่าหากคุณใช้แนวทางนี้จะเป็นการดีกว่าที่จะฉีดโรงงานลงใน MyClass เมื่อถูกสร้างขึ้นแทนที่จะส่งผ่านเข้าไปในวิธีการของคุณทุกครั้งที่มีการเรียกใช้

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

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

1
แนวทางที่ดีและไม่สะท้อนแสง การสะท้อนไม่ได้เป็นทางเลือกเสมอไป myMethod ควรสามารถรับ MyFactory ได้ <? ขยาย T> ใช่ไหม?
erickson

1
การโทรที่ดี - คุณจะต้องใส่ไวด์การ์ดที่มีขอบเขตบนโรงงานเพื่ออนุญาตให้สร้างอ็อบเจ็กต์ประเภท T และคลาสย่อยของ T ใน myMethod ()
Dan Hodge


0

ทางเลือกหนึ่งคือการใช้ Object

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

ในตอนแรกจะเป็นประเภท Object แต่จากนั้นจะโยนลงไปที่พิมพ์ T นี่เป็นสิ่งที่น่าเกลียดเพราะการลดการแคสต์ให้เหลือศูนย์คือเป้าหมายที่ควรจะเป็นสำหรับการเริ่มต้นอ็อบเจ็กต์ แต่ฉันคิดว่ามันจะได้ผล


-2

คลาส classOfT

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

2
classOfT class นั้นประกาศที่ไหน?
Fran Marzoa
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.