سؤال

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

الشيء الوحيد الذي يمكنني القيام به هو للتحقق من عدد قليل معروفة ثابتة أنواع:

private static final Set<Class> knownImmutables = new HashSet<Class>(Arrays.asList(
        String.class, Byte.class, Short.class, Integer.class, Long.class,
        Float.class, Double.class, Boolean.class, BigInteger.class, BigDecimal.class
));

...

public static boolean isImmutable(Object o) {
    return knownImmutables.contains(o.getClass());
}

هذا في الواقع يحصل علي 90% من الطريق ، ولكن في بعض الأحيان يمكن للمستخدمين سوف ترغب في إنشاء بسيطة ثابتة أنواع من تلقاء نفسها:

public class ImmutableRectangle {
    private final int width;
    private final int height;
    public ImmutableRectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }
    public int getWidth() { return width; }
    public int getHeight() { return height; }
}

هل هناك طريقة (ربما باستخدام التفكير) التي لا يمكن أن يعتمد عليه في كشف ما إذا كانت الدرجة غير قابل للتغيير?ايجابيات كاذبة (التفكير انها ثابتة عندما لا) ليست مقبولة ولكن السلبيات كاذبة (التفكير انها قابلة للتغيير عندما لا) هي.

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

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

المحلول

ليس هناك طريقة يمكن الاعتماد عليها للكشف عن ما إذا كان الصف غير قابل للتغيير.هذا لأن هناك الكثير من الطرق عقار من فئة قد تكون غيرت و لا يمكنك كشف كل منهم عن طريق التفكير.

الطريقة الوحيدة للحصول على مقربة من هذا هو:

  • فقط تسمح النهائي خصائص الأنواع التي هي ثابتة (أنواع بدائية و دروس تعلم هي ثابتة),
  • تتطلب فئة النهائية نفسها
  • تتطلب فإنها ترث من الفئة الأساسية التي تقدمها (الذي هو ضمان أن تكون غير قابلة للتغيير)

ثم يمكنك التحقق من التعليمة البرمجية التالية إذا كان الهدف لديك هو غير قابل للتغيير:

static boolean isImmutable(Object obj) {
    Class<?> objClass = obj.getClass();

    // Class of the object must be a direct child class of the required class
    Class<?> superClass = objClass.getSuperclass();
    if (!Immutable.class.equals(superClass)) {
        return false;
    }

    // Class must be final
    if (!Modifier.isFinal(objClass.getModifiers())) {
        return false;
    }

    // Check all fields defined in the class for type and if they are final
    Field[] objFields = objClass.getDeclaredFields();
    for (int i = 0; i < objFields.length; i++) {
        if (!Modifier.isFinal(objFields[i].getModifiers())
                || !isValidFieldType(objFields[i].getType())) {
            return false;
        }
    }

    // Lets hope we didn't forget something
    return true;
}

static boolean isValidFieldType(Class<?> type) {
    // Check for all allowed property types...
    return type.isPrimitive() || String.class.equals(type);
}

تحديث: كما اقترح في التعليقات ، يمكن أن تمتد إلى recurse على الفائقة بدلا من التحقق لفئة معينة.واقترح أيضا بشكل متكرر استخدام isImmutable في isValidFieldType الأسلوب.هذا ربما يمكن أن تعمل كما فعلت بعض التجارب.ولكن هذه ليست تافهة.لا يمكنك التحقق من جميع أنواع الحقول مع دعوة إلى isImmutable لأن السلسلة بالفعل فشل في هذا الاختبار (الميدانية hash ليست نهائية!).أيضا يمكنك بسهولة تشغيل لا نهاية لها في recursions ، مما تسبب StackOverflowErrors ;) مشاكل أخرى قد يكون سببها الوراثة ، حيث لديك أيضا إلى التحقق من أنواع immutablity.

أعتقد مع بعض العمل ، هذه المشاكل المحتملة يمكن حلها بطريقة أو بأخرى.ولكن بعد ذلك, عليك أن تسأل نفسك أولا إذا كان هو حقا يستحق ذلك (أيضا أداء الحكمة).

نصائح أخرى

استخدام غير قابل للتغيير الشرح من جافا التزامن في الممارسة.أداة FindBugs ثم يمكن أن تساعد في الكشف عن الطبقات التي هي قابلة للتغيير ولكن لا ينبغي أن يكون.

في شركتي لقد عرف سمة يسمى @Immutable.إذا اخترت أن نعلق ذلك إلى فئة يعني هل تعدني أنك غير قابل للتغيير.

يعمل من أجل التوثيق في حالة أنه يعمل كعامل تصفية.

بالطبع أنت لا تزال اعتمادا على الكاتب حفظ كلمته عن كونها ثابتة ، ولكن منذ المؤلف صراحة وأضاف الشرح هو افتراض معقول.

في الأساس لا يوجد.

يمكنك بناء العملاقة القائمة البيضاء من قبول الطبقات ولكن أعتقد أن أقل مجنون طريقة الكتابة فقط في وثائق جمع كل شيء أن يذهب هو هذه المجموعة يجب أن أن تكون ثابتة.

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

قد يكون هذا تلميح آخر:

إذا كانت فئة لا واضعي ثم فإنه لا يمكن أن يكون تحور منح المعلمات تم إنشاؤه مع إما "بدائية" أو أنواع غير قابلة للتغيير أنفسهم.

أيضا هناك طرق يمكن أن تنقضها جميع المجالات النهائية والخاصة ،

سأحاول أن الرمز شيء غدا لك لكن سيمون التعليمات البرمجية باستخدام انعكاس تبدو جيدة جدا.

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

البند يسمى:"صالح ثبات"

الرابط:http://java.sun.com/docs/books/effective/

في قانون بلدي, أنا خلق مجموعة من الكائنات التي سيتم الوصول إليها من قبل مختلف المواضيع بطريقة آمنة فقط إذا كانت الأشياء غير قابلة للتغيير.

لا إجابة مباشرة على سؤالك, ولكن نأخذ في الاعتبار أن الكائنات التي هي ثابتة لا تلقائيا ضمان أن تكون موضوع آمنة (للأسف).رمز يحتاج إلى أن الآثار الجانبية الحرة أن تكون موضوع آمنة ، وهذا هو قليلا أكثر صعوبة.

افترض أن لديك هذه الفئة:

class Foo {
  final String x;
  final Integer y;
  ...

  public bar() {
    Singleton.getInstance().foolAround();
  }
}

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

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

أيضا كن حذرا حول متكرر التحقق من الحقول التابعة ل ثبات, كنت قد ينتهي مع الدوائر:

class A {
  final B b; // might be immutable...
}

class B {
  final A a; // same so here.
}

فئات A و B هي تماما غير قابل للتغيير (وربما حتى للاستخدام من خلال بعض التفكير الخارقة) ، ولكن من السذاجة العودية رمز سوف تذهب إلى حلقة لا نهاية لها التحقق من A ثم B ثم مرة أخرى, فصاعدا إلى B ، ...

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

يمكنك أن تسأل العملاء لإضافة البيانات الوصفية (الشروح) والتحقق منها في وقت التشغيل مع انعكاس مثل هذا:

البيانات الوصفية:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CLASS)
public @interface Immutable{ }

رمز العميل:

@Immutable
public class ImmutableRectangle {
    private final int width;
    private final int height;
    public ImmutableRectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }
    public int getWidth() { return width; }
    public int getHeight() { return height; }
}

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

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

يمكنك استخدام اوب ، @Immutable الشرح من jcabi-الجوانب:

@Immutable
public class Foo {
  private String data;
}
// this line will throw a runtime exception since class Foo
// is actually mutable, despite the annotation
Object object = new Foo();

مثل غيرها من answerers ، IMHO ليس هناك طريقة يمكن الاعتماد عليها لمعرفة ما إذا كان كائن هو حقا غير قابل للتغيير.

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

interface Immutable {}

class MyImmutable implements Immutable{...}

public void add(Object o) {
  if (!(o instanceof Immutable) && !checkIsImmutableBasePrimitive(o))
    throw new IllegalArgumentException("o is not immutable!");
  ...
}

جرب هذا:

public static boolean isImmutable(Object object){
    if (object instanceof Number) { // Numbers are immutable
        if (object instanceof AtomicInteger) {
            // AtomicIntegers are mutable
        } else if (object instanceof AtomicLong) {
            // AtomLongs are mutable
        } else {
            return true;
        }
    } else if (object instanceof String) {  // Strings are immutable
        return true;
    } else if (object instanceof Character) {   // Characters are immutable
        return true;
    } else if (object instanceof Class) { // Classes are immutable
        return true;
    }

    Class<?> objClass = object.getClass();

    // Class must be final
    if (!Modifier.isFinal(objClass.getModifiers())) {
            return false;
    }

    // Check all fields defined in the class for type and if they are final
    Field[] objFields = objClass.getDeclaredFields();
    for (int i = 0; i < objFields.length; i++) {
            if (!Modifier.isFinal(objFields[i].getModifiers())
                            || !isImmutable(objFields[i].getType())) {
                    return false;
            }
    }

    // Lets hope we didn't forget something
    return true;
}

على حد علمي لا توجد طريقة لتحديد ثابتة الكائنات هذا هو الصحيح 100%.ومع ذلك, كنت قد كتبت مكتبة تحصل أقرب.ينفذ تحليل بايت كود من فئة لتحديد ما إذا كان هو غير قابل للتغيير أو لا يمكن تنفيذها في وقت التشغيل.هو على الجانب الصارم ، لذلك كما يسمح القائمة البيضاء معروفة ثابتة الطبقات.

يمكنك التحقق من ذلك في: www.mutabilitydetector.org

فإنه يسمح لك لكتابة التعليمات البرمجية مثل هذا في التطبيق الخاص بك:

/*
* Request an analysis of the runtime class, to discover if this
* instance will be immutable or not.
*/
AnalysisResult result = analysisSession.resultFor(dottedClassName);

if (result.isImmutable.equals(IMMUTABLE)) {
    /*
    * rest safe in the knowledge the class is
    * immutable, share across threads with joyful abandon
    */
} else if (result.isImmutable.equals(NOT_IMMUTABLE)) {
    /*
    * be careful here: make defensive copies,
    * don't publish the reference,
    * read Java Concurrency In Practice right away!
    */
}

هو مجاني و مفتوح المصدر تحت رخصة أباتشي 2.0.

تحقق من جو-e, ، تنفيذ قدرات جافا.

الأمر الذي يعمل على نسبة عالية من builtin الطبقات هو اختبار instanceof قابلة للمقارنة.عن الطبقات التي ليست ثابتة مثل التاريخ ، وغالبا ما تعامل على أنها ثابتة في معظم الحالات.

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

(ملاحظة:هذا هو نسخة من تعليقي هنا: https://stackoverflow.com/a/28111150/773113)

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

ثبات فئة أربع القيم الممكنة: Unknown, Mutable, Immutable, ، Calculating.سوف ربما تريد أن يكون لها خريطة الزميلة كل فئة أن كنت قد واجهت حتى الآن إلى ثبات قيمة.وبطبيعة الحال ، Unknown في الواقع لا تحتاج إلى أن تنفذ ، لأنه سوف يكون ضمنيا الدولة من أي الطبقة التي ليست بعد في الخريطة.

لذا عندما تبدأ دراسة الفصل ربطه مع Calculating القيمة في الخريطة, عند الانتهاء, يمكنك استبدال Calculating إما Immutable أو Mutable.

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

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

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

عند دراسة فئة ما تريد القيام به أولا هو recurse لتحديد ثبات لها super فئة.إذا كان السوبر هي قابلة للتغيير ، ثم سليل هو تعريف قابلة للتغيير أيضا.

ثم تحتاج فقط إلى التحقق من أعلن مجالات ذات كل المجالات.

إذا كان الحقل غير النهائية ، ثم فئة غير قابلة للتغيير.

إذا كان الحقل هو النهائي, ولكن نوع من الميدان قابلة للتغيير ، ثم الفئة هي قابلة للتغيير.(المصفوفات هي بحكم التعريف قابلة للتغيير.)

إذا كان الحقل هو النهائي و نوع الحقل Calculating, ثم تجاهله والمضي قدما إلى الحقل التالي.إذا كان كل المجالات إما ثابتة أو Calculating, ثم الفئة غير قابل للتغيير.

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

بعض الدروس قد تحتوي على غير النهائية المجالات و لا يزال فعالية ثابتة.مثال على ذلك هو String فئة.الفئات الأخرى التي تندرج تحت هذه الفئة هي الطبقات التي تحتوي على غير النهائية أعضاء بحتة لرصد الأداء الأغراض (الاحتجاج العدادات ، الخ) ، الطبقات التي تنفذ المصاصة ثبات (ابحث عنه) ، الطبقات التي تحتوي على الأعضاء التي هي الواجهات التي هي معروفة لا تسبب أي آثار جانبية.أيضا ، إذا صف يحتوي على حسن النية قابلة للتغيير المجالات ولكن وعود بعدم أخذها في الاعتبار عند حساب hashCode() و يساوي () ، ثم فئة هو بالطبع غير آمنة عندما يتعلق الأمر متعدد خيوط, ولكن لا يزال يمكن أن تعتبر ثابتة بغرض استخدامه كوسيلة أساسية في الخريطة.لذا كل هذه الحالات يمكن أن تعالج في واحدة من طريقتين:

  1. يدويا إضافة فصول دراسية (واجهات) إلى ثبات كاشف.إذا كنت تعرف أن فئة معينة على نحو فعال ثابتة على الرغم من حقيقة أن ثبات اختبار فشلت ، يمكنك يدويا إضافة إدخال إلى مكشاف مع الزميلة Immutable.هذه الطريقة ، كاشف يحاول أبدا أن تحقق ما إذا كان هو غير قابل للتغيير ، وسوف دائما تقول نعم ، هو.'

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

آمل أن يساعد هذا للأجيال القادمة.

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