سؤال

لنفترض أنا باستخدام واجهة مع معلمة نوع عام

interface Foo<T> {
  T getOne();
  void useOne(T t);
}

والقصد من ذلك هو أن هذا النوع T غير مجردة: كان يفرض قيدا نوع على تطبيقات Foo، ولكن رمز العميل لا يهمه بالضبط ما هو T

وهذه ليست مشكلة في سياق أسلوب عام:

public <T> void doStuff(Foo<T> foo) {
  T t = foo.getOne();
  /* do stuff */
  foo.useOne(t);
}

ولكن لنفترض أنني أريد لتفريق عمل doStuff، وتوفير بعض الدول في Bar الصف. في هذه الحالة، يبدو لي أن تحتاج إلى إضافة المعلمة نوع Foo إلى Bar.

public class Bar<T> {
  private Foo<T> foo;
  private T t;

  /* ... */

  public void startStuff() {
    t = foo.getOne();
  }

  public void finishStuff() {
    foo.useOne(t);
  }
}

وهذا هو نوع من غريب، لأن لا يظهر T نوع المعلمة في الواجهة العمومية من Bar (أي لا يتم تضمينه في أي معلمة أسلوب أو العودة النوع). هل هناك طريقة ل"تحديد T بعيدا"؟ أي، هل يمكنني اتخاذ الترتيبات اللازمة لT المعلمة أن تكون مخفية في واجهة Bar، كما هو الحال في ما يلي؟

public class Bar {
  <T> { // foo and t have to use the same T
    private Foo<T> foo;
    private T t;
  } // T is out of scope
  ... 
}
هل كانت مفيدة؟

المحلول

والمشكلة هي مماثلة لتلك التي حلها عن طريق التقاط " المساعد "، ولكن لست متأكدا من أنه يمكن تطبيقها على سبيل المثال الثاني حيث تستخدم طريقتين منفصلة. أول طريقة doStuff الخاص يمكن أن يكون بالتأكيد أفضل كما هو مكتوب public void doStuff(Foo<?> foo)، لأنه يعمل بغض النظر نوع Foo المعلمة. ثم، فإن "الاستيلاء المساعد" نمط تكون مفيدة.


تحديث: بعد ترقيع قليلا، وتوسيع فكرة المساعد القبض جويتز، خطرت لي هذه. في الداخل، يبدو قليلا الفوضى. من الخارج، وكنت لا تشك في وجود شيء.

public class Bar {
  private final Helper<?> helper;
  public Bar(Foo<?> foo) {
    this.helper = Helper.create(foo);
  }
  public void startStuff() {
    helper.startStuff();
  }
  public void finishStuff() {
    helper.finishStuff();
  }
  private static class Helper<T> {
    private final Foo<T> foo;
    private T t;
    private Helper(Foo<T> foo) {
      this.foo = foo;
    }
    static <T> Helper<T> create(Foo<T> foo) {
      return new Helper<T>(foo);
    }
    void startStuff() {
      t = foo.getOne();
    }
    void finishStuff() {
      foo.useOne(t);
    }
  }
}

نصائح أخرى

ليكون من المفيد، في مرحلة ما أنت ذاهب لتعيين الحقل foo. عند هذه النقطة يجب أن نعرف (أو تكون قادرة على التقاط) T. أود أن أقترح القيام بذلك في المنشئ، ومن ثم فمن المنطقي لBar أن يكون معلمة عامة. حتى انك تستطيع استخدام واجهة بحيث لا يكون رمز العميل لمعرفة نوع. ومع ذلك، أفترض أنك لن تأخذ نصيحتي وتريد حقا setFoo. حتى مجرد إضافة نقطة إلى تنفيذ للتحويل:

/* pp */ class final BarImpl<T> {
    private final Foo<T> foo;
    private T t;

    BarImpl(Foo<T> foo) {
        this.foo = foo;
    }

    public void startStuff() {
        t = foo.getOne();
    }

    public void finishStuff() {
        foo.useOne(t);
    }
}

public final class Bar {
    private BarImpl<?> impl;

    /* ... */

    // Need to capture this wildcard, because constructors suck (pre-JDK7?).
    public void setFoo(Foo<?> foo) {
        setFooImpl(foo);
    }
    private <T> void setFooImpl(Foo<T> foo) {
        impl = new BarImpl<T>(foo);
    }

    public void startStuff() {
        impl.startStuff();
    }

    public void finishStuff() {
        impl.finishStuff();
    }
}

لماذا لا يكون تسلسل هرمي ثلاثي:

abstract class Foo

abstract class FooImplBase<T> extends Foo

class Bar extends FooImplBase<String>

والعملاء لا يعرفون سوى عن Foo، والتي لا تحتوي على أي الطرق العامة. إدخال أي طرق العامة التي تحتاج إليها في FooImplBase<T> ثم تستمد من فئة محددة منه.

وهكذا في حياتك سبيل المثال startStuff() وendStuff() ستكون مجردة في Foo وتنفيذها في FooImplBase<T>. هل هذا الصوت مثل ذلك يمكن أن تعمل في الوضع الحقيقي الخاص بك؟ وأنا أتفق انها مرهقة بعض الشيء.

وأنت تحديد الدرجة بار. شيئين صحيحة ...

1) ليس هناك نوع حدودي تشارك في بار. وهذا هو، فو وأعضاء ر لها نوع واحد، ويقول U، وهذا هو الثابت عن التعريف. إذا كنت تمرير فو التي تتوقعها لتعيين فو، يجب أن يكون فو . إذا كان هذا هو كل شيء صحيح، ثم نعم، انها ليست جزءا من الواجهة العمومية - كل شيء له نوع معين. ثم، وأنا لست متأكدا ما تقصد ب "قياس"، كما أن هناك أي نوع المتغيرات الحرة. إذا كنت تعني قياس عالميا، كيف يمكن التوفيق بين كون بار لا يوجد لديه الكتابة حدودي، لذلك يجب أن يكون إعطاء نوع ملموسة لكل من انها الأعضاء؟

2) هناك نوع حدودي تشارك في بار. قد لا يكون من الواضح في الواجهة العمومية، ولكن ربما كنت تمر في فو ، لذلك كنت تريد أن يكون إنشاء مثيل فئة بار مع أكثر من نوع واحد. بعد ذلك، كما ذكرت، وهذا هو في الواجهة العمومية، وتحتاج إلى إجراء بار حدودي في T مع الأدوية. هذا يمنحك شكلا من أشكال تقدير عالمي لتعريف "لجميع أنواع T، هذا التعريف هو الصحيح".

وفي مكان ما يجب أن تقرر ما النوع الذي تريد استخدامه ل'T' داخل الطبقة بار. فإما يجب اختيار في تعريف بار (استبدال فو التي كتبها فو داخل تعريف فئة) أو ترك الأمر للعميل من الدرجة بار: في هذه الحالة بار يجب أن يكون عاما

إذا كنت تريد أن يكون واجهة لشريط لا تعتمد على T <م> و تكون قادرة على اختيار أنواع مختلفة لT، يجب عليك استخدام واجهة غير عامة أو فئة قاعدة مجردة، كما في:

interface Bar {
  void startStuff();
  // ...
}

class BarImplement<T> {
  private Foo<T> foo;
  // ...
}

إذا كنت تريد حقا أن يوجه بعض الغضب، يمكنك وضع الطبقة بار داخل واجهة فو، وتمتص T من هذا النحو. انظر هذه المقالة لمزيد من المعلومات حول فئات داخل واجهات . ربما هذا هو حالة واحدة حيث أن من المنطقي؟

interface Foo<T> {
  T getOne();
  void useOne(T t);

  public class Bar<T> {
    private Foo<T> foo;
    private T t;
    public void startStuff() {
      t = foo.getOne();
    }
    public void finishStuff() {
      foo.useOne(t);
    }
  }
}

والمعلمة T في هذه الحالة تصبح عديمة الفائدة بقدر ما تشعر بالقلق بار، لأنها سوف تمحى لكائن في وقت الترجمة. لذلك يمكن أن أيضا "توفر على نفسك عناء"، والقيام المحو المبكر:

public class Bar {

    private Foo<Object> foo;
    private Object t;

  ... 
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top