سؤال

وكنت أعتقد أنني فهمت الوراثة جافا بشكل جيد، ولكن بعد ذلك جئت عبر ما يلي في java.lang.Enum:

class Enum<E extends Enum<E>>

ويمكن للشخص أن يفسر كيفية تفسير هذا النوع المعلمة؟ نقاط المكافأة لتقديم أمثلة أخرى من حيث يمكن استخدام نوع المعلمة مماثلة.

هل كانت مفيدة؟

المحلول

وهذا يعني أن الحجة نوع لالتعداد يجب أن تستمد من التعداد الذي في حد ذاته لديه نفس نوع الوسيطة. كيف يمكن أن يحدث هذا؟ من خلال جعل نوع الوسيطة نوع جديد نفسه. حتى لو كنت قد حصلت على التعداد دعا StatusCode، فإنه يكون معادلا ل:

public class StatusCode extends Enum<StatusCode>

والآن إذا كنت تحقق من القيود، لدينا Enum<StatusCode> - حتى E=StatusCode. دعونا تحقق: لا E تمديد Enum<StatusCode>؟ نعم! نحن بخير.

قد يكون جيدا تسأل نفسك ما الهدف من هذا هو :) حسنا، فهذا يعني أن API لالتعداد يمكن أن يشير إلى نفسه - على سبيل المثال، أن تكون قادرة على القول بأن Enum<E> تنفذ Comparable<E>. الفئة الأساسية هي قادرة على القيام المقارنات (في حالة تتضمن التعدادات) ولكن لا يمكن التأكد من أن يقارن فقط الحق في نوع من تتضمن التعدادات مع بعضها البعض. (تحرير: حسنا، ما يقرب - انظر تحرير في الجزء السفلي)

ولقد استعملت شيئا من هذا القبيل في بلدي C # ميناء ProtocolBuffers. هناك "رسائل" (ثابت) و "بناة" (قابلة للتغيير، وتستخدم لبناء الرسالة) - وأنها تأتي كما أزواج من أنواع. واجهات المعنية هي:

public interface IBuilder<TMessage, TBuilder>
  where TMessage : IMessage<TMessage, TBuilder> 
  where TBuilder : IBuilder<TMessage, TBuilder>

public interface IMessage<TMessage, TBuilder>
  where TMessage : IMessage<TMessage, TBuilder> 
  where TBuilder : IBuilder<TMessage, TBuilder>

وهذا يعني أنه من رسالة يمكنك الحصول على باني المناسب (على سبيل المثال، لأخذ نسخة من رسالة وتغيير بعض بت) ومن البناء يمكنك الحصول على الرسالة المناسبة عند الانتهاء من بنائه. انها مستخدمين بعمل جيد من API لا تحتاج لرعاية فعلا عن هذا على الرغم من - الأمر معقد بشكل مرعب، وتولى عدة مرات التكرار للحصول على ما هو عليه

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

وحتى إذا لم يتم التعامل مع Enum "خصيصا" في جاوة على أي حال، هل يمكن (كما ورد في تعليق) إنشاء الأنواع التالية:

public class First extends Enum<First> {}
public class Second extends Enum<First> {}

وSecond ستنفذ Comparable<First> بدلا من Comparable<Second> ... ولكن First نفسه سيكون على ما يرام.

نصائح أخرى

وفيما يلي نسخة معدلة من التفسير من كتاب <م> جافا الوراثة ومجموعات : لقد أعلن أحد Enum

enum Season { WINTER, SPRING, SUMMER, FALL }

والتي سيتم توسيعها لتشمل فئة

final class Season extends ...

وحيث ... هو أن تكون الفئة الأساسية parameterised بطريقة أو بأخرى لتتضمن التعدادات. دعونا نعمل ما الذي يجب أن يكون. حسنا، واحدة من متطلبات Season هو أنه ينبغي أن تنفذ Comparable<Season>. لذلك نحن ذاهبون الى الحاجة

Season extends ... implements Comparable<Season>

وماذا يمكن أن تستخدمها ل... التي من شأنها أن تسمح لهذا العمل؟ نظرا لأنه يجب أن يكون هناك البارامترات من Enum، والخيار الوحيد هو Enum<Season>، حتى أنه يمكن أن يكون:

Season extends Enum<Season>
Enum<Season> implements Comparable<Season>

وهكذا هو parameterised Enum على أنواع مثل Season. مجردة من Season و تحصل أن المعلمة من Enum هو أي نوع يرضي

 E extends Enum<E>

وموريس نافطالين (المؤلف المشارك، جافا الوراثة والمجموعات)

وهذا يمكن أن يتضح من مثال بسيط وتقنية والتي يمكن استخدامها لتنفيذ أسلوب بالسلاسل يدعو لفئات فرعية. في المثال التالي setName بإرجاع Node ذلك سوف تسلسل لا تعمل لCity:

class Node {
    String name;

    Node setName(String name) {
        this.name = name;
        return this;
    }
}

class City extends Node {
    int square;

    City setSquare(int square) {
        this.square = square;
        return this;
    }
}

public static void main(String[] args) {
    City city = new City()
        .setName("LA")
        .setSquare(100);    // won't compile, setName() returns Node
}

وحتى نتمكن من الرجوع إلى الدرجة الفرعية في اعلان عام، بحيث City الآن إرجاع النوع الصحيح:

abstract class Node<SELF extends Node<SELF>>{
    String name;

    SELF setName(String name) {
        this.name = name;
        return self();
    }

    protected abstract SELF self();
}

class City extends Node<City> {
    int square;

    City setSquare(int square) {
        this.square = square;
        return self();
    }

    @Override
    protected City self() {
        return this;
    }

    public static void main(String[] args) {
       City city = new City()
            .setName("LA")
            .setSquare(100);                 // ok!
    }
}

وأنت لست الوحيد يتساءل ماذا يعني ذلك. يمكنك الاطلاع على الفوضى جافا بلوق .

واضاف "اذا فئة تمتد هذه الفئة، وينبغي أن تمرير معلمة E. المعلمة حدود E هي للفئة التي تمتد هذه الفئة مع نفس المعلمة E".

لقد أوضحت هذه الوظيفة تماما بالنسبة لي هذه مشكلة "الأنواع العامة متكررة. أردت فقط أن أضيف حالة أخرى حيث ضروري هذا الهيكل خاص.

وافترض أن لديك العقد عامة في رسم بياني عام:

public abstract class Node<T extends Node<T>>
{
    public void addNeighbor(T);

    public void addNeighbors(Collection<? extends T> nodes);

    public Collection<T> getNeighbor();
}

وبعد ذلك يمكن أن يكون لديك الرسوم البيانية من أنواع المتخصصة:

public class City extends Node<City>
{
    public void addNeighbor(City){...}

    public void addNeighbors(Collection<? extends City> nodes){...}

    public Collection<City> getNeighbor(){...}
}

إذا نظرتم الى شفرة المصدر Enum، فمن لديه التالية:

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {

    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }

    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
    }

    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    } 
}

وأول شيء أولا، ماذا يعني E extends Enum<E>؟ فهذا يعني أن المعلمة نوع هو الشيء الذي يمتد من التعداد، وليس parametrized مع نوع الخام (هو parametrized من قبل نفسه).

وهذا أمر مهم إذا كان لديك التعداد

public enum MyEnum {
    THING1,
    THING2;
}

والتي، لو كنت أعرف بشكل صحيح، ويترجم إلى

public final class MyEnum extends Enum<MyEnum> {
    public static final MyEnum THING1 = new MyEnum();
    public static final MyEnum THING2 = new MyEnum();
}

وهكذا وهذا يعني أن MyEnum يتلقى الطرق التالية:

public final int compareTo(MyEnum o) {
    Enum<?> other = (Enum<?>)o;
    Enum<MyEnum> self = this;
    if (self.getClass() != other.getClass() && // optimization
        self.getDeclaringClass() != other.getDeclaringClass())
        throw new ClassCastException();
    return self.ordinal - other.ordinal;
}

والأهم من ذلك،

    @SuppressWarnings("unchecked")
    public final Class<MyEnum> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<MyEnum>)clazz : (Class<MyEnum>)zuper;
    }

وهذا يجعل getDeclaringClass() المدلى بها إلى كائن Class<T> الصحيح.

والمثال هناك طريقة أكثر وضوحا هو الذي أجبت على هذا السؤال حيث لا يمكن تجنب هذا بناء إذا كنت ترغب في تحديد ملزمة عام.

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