variable - java static




هل من المبرر على الإطلاق أن يكون لكائن ما هو نفسه حقل؟ (6)

هل من المبرر على الإطلاق أن يكون لكائن يحتوي على نفسه كحقل مثل هذا:

class Thing {

    Thing field;

    public Thing() {
        this.field = this;
    }
}

أنا لا أتحدث عن فصل دراسي يحتوي على حقل من نفس النوع ولكن تم إعداد فصل دراسي بحيث يكون لكل مثيل في الفصل نفسه كحقل. لقد رأيت هذا في بعض التعليمة البرمجية القديمة (لم يتم استخدام هذا الحقل أبدًا) لذا فأنا أشعر بالفضول. أي استخدام شرعي لهذا؟


حالة من التأمل مع ضوابط واجهة مستخدم المكتبة.

إنه مشروع بالكامل في أنماط استخدام معينة. في إطار عمل غير معروف ، رأيت XML مثل هذا المثال المبسط:

<form>
    <comboBox listItems="order.LineItems" displayMember="description" valueMember="selfRef"/>
</form>

يتم تثبيت روابط التحكم على القيم من خلال Reflection . إذا كنت راضيًا باستخدام Integer ID فقط باعتباره valueMember ، فلا تحتاج إلى مرجع عضو في الفئة إلى الكائن نفسه. ولكن إذا كنت ترغب في الإشارة إلى الكائن نفسه وبحسب التصميم ، لا يتم تطبيق القيمة الفارغة ( "" ) كحالة خاصة تعني this ، فإنك تحتاج إلى الاحتفاظ بهذا في متغير عضو ( selfRef في المثال أعلاه).


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

public static void doItToIt(Foo it) {
  if (it.getType().equals("bar")) {
    it.setSomeVariable(value);
    it.manipulate();
  } else {
    // do nothing
  }
}

... لذلك قررنا التخلص من getType() وجعلها علاقة من فئة فرعية ، وعندما تم إعادة تشكيلها ، تم نسخ الشيء ولصقه لجعل الشفرة أكثر نظافة وأكثر توجهاً نحو الهدف ، مما يستدعي:

public class Bar extends Foo {
    private Bar it = this;

    private String getType() {
      return "bar";
    }

    public void doItToIt() {
      if (it.getType().equals("bar")) {
        it.setSomeVariable(value);
        it.manipulate();
      } else {
        // do nothing
      }
    }
 }

إذا لم يكن هناك سوى طريقة واحدة كهذه ، فمن الأفضل استخدام متغير محلي:

public void doItToIt() {
  final Bar it = this;
  ...
}

ولكن إذا كان هناك عدة ، فإن استخدام متغير مثيل سيحصل على الشفرة بشكل أسرع.

سيكون من الأفضل الآن أن نذهب getType() this (والتخلص من getType() ، وإزالة if ، وهكذا دواليك) لكن كخطوة وسيطة ، لا يزال هناك تحسن ، وقد يتم تركه في ذلك إذا كان هناك (أو يُعتقد أنها) أشياء أخرى أكثر أهمية يجب القيام بها.

هناك إمكانية أخرى تتمثل في نسخ شفرة زائفة من شيء ما مثل مواصفة يكون this الاسم فيها اسم في الرمز الزائف وتحاول عمداً مطابقة بنية الشفرة الزائفة لتسهيل مقارنة التطبيق بالمواصفات. (ومرة أخرى ، إذا كان لديك طرق متعددة ، فإن استخدام متغير محلي يؤدي إلى التكرار.)

ولكن بشكل عام ، لا ، فامتلاك متغير مثال يشير دائمًا إلى this الأمر زائد عن الحاجة وبالتالي يعيق الوضوح.


لا ، في رأيي هذا غير مبرر أبداً ، لأن كل كائن يتضمن بالفعل مثل هذا المجال: this . بالطبع ، يكون مبررًا أن يحتوي كائن ما على حقل قد يشير في بعض الأحيان ، لكن ليس دائمًا ، إلى نفسه (والذي يمكن أن يحدث على سبيل المثال في قائمة مرتبطة دورية) ، ولكن السؤال هو حول حقل يشير دائمًا إلى الكائن بحد ذاتها.

لقد رأيت رمزًا حيث يتم استخدام مثل هذا الحقل ليتمكّن من الإشارة إلى الكائن المحتوي من (مجهول) من الطبقات الداخلية ، ولكن أيضًا في هذه الحالة ليس ضروريًا. يمكنك استخدام ContainingClass.this . فمثلا:

class A {
    class B {
        A getParent() {
            return A.this;
        }
    }
}

لا لأنه سيكون من المكرر وجود نفسه كحقل عندما يكون لديه الكلمة الرئيسية this

ضمن أسلوب مثيل أو مُنشئ ، this هو مرجع للكائن الحالي - الكائن الذي يتم استدعاء أسلوبه أو مُنشئه. يمكنك الرجوع إلى أي عضو من الكائن الحالي من خلال طريقة مثيل أو منشئ باستخدام this . "- Javadocs Keyword" this "


نعم ، على الرغم من هذا نادر. يستخدم هذا في JDK للحالات التي يمكن أن يكون this ولكن قد لا يكون.

من الفصل الذي يطبق Collections.synchronizedCollection(c)

static class SynchronizedCollection<E> implements Collection<E>, Serializable {
    private static final long serialVersionUID = 3053995032091335093L;

    final Collection<E> c;  // Backing Collection
    final Object mutex;     // Object on which to synchronize

    SynchronizedCollection(Collection<E> c) {
        this.c = Objects.requireNonNull(c);
        mutex = this;
    }

    SynchronizedCollection(Collection<E> c, Object mutex) {
        this.c = Objects.requireNonNull(c);
        this.mutex = Objects.requireNonNull(mutex);
    }

في هذه الحالة ، قد يكون mutex هو الفصل الحالي ، ولكن إذا تم الحصول على هذه المجموعة من مجموعة متزامنة موجودة ، فقد يكون كائن المزامنة مختلفًا. على سبيل المثال ، إذا قمت بالاتصال بـ Map.values() وكانت Map synchronizedMap ، فسيكون mutex هو الخريطة وليس المجموعة.

مثال آخر هو Throwable الذي يشير إلى نفسه كسبب افتراضي.

/**
 * The throwable that caused this throwable to get thrown, or null if this
 * throwable was not caused by another throwable, or if the causative
 * throwable is unknown.  If this field is equal to this throwable itself,
 * it indicates that the cause of this throwable has not yet been
 * initialized.
 *
 * @serial
 * @since 1.4
 */
private Throwable cause = this;

يمكن تبريرها إذا كان يمكن تغيير الكائن المشار إليه بعد ذلك ، ولكن القيمة الأولية هي (على الرغم من أنه يجب إعادة النظر في مثل هذا التصميم أيضًا ، لأنه قد يشير إلى أن Thing لديه مسؤوليات أكثر مما ينبغي).

ومع ذلك ، إذا كان الكائن المشار إليه دائمًا ، فهذا غير ضروري ومربك.

الإعلان التالي ليس فقط مربكا ، ولكن أيضا مضحك :)

private final Thing thing = this;





this