الأسلوب الحمائم الزائد - الأسلوب الخاص يسبب فشل تجميع - هل هذا غريب JLS أو علة Javac؟

StackOverflow https://stackoverflow.com/questions/1852939

سؤال

لقد صادفت غرقا من JLS، أو علة Javac (غير متأكد من ذلك). يرجى قراءة ما يلي وتقديم شرح، نقلا عن مرور JLS أو معرف Sun Bug، حسب الاقتضاء.

لنفترض أن لدي مشروع مفاهيم برمز في ثلاثة "وحدات" -

  1. API - يحدد إطار واجهة برمجة تطبيقات الإطار - اعتقد Servlet API
  2. Imply - يحدد تنفيذ API - فكر في حاوية Servlet Tomcat
  3. التطبيق - التطبيق كتبته

فيما يلي الطبقات في كل وحدة:

API - MessagePrinter.java

package api;

public class MessagePrinter {

    public void print(String message) {
        System.out.println("MESSAGE: " + message);
    }
}

API - MessageHolder.java (نعم، إنه يشير إلى فئة "آمنة" - أكثر في وقت لاحق)

package api;

import impl.MessagePrinterInternal;

public class MessageHolder {

    private final String message;

    public MessageHolder(String message) {
         this.message = message;
    }

    public void print(MessagePrinter printer) {
        printer.print(message);
    }

    /**
     * NOTE: Package-Private visibility.
     */ 
    void print(MessagePrinterInternal printer) {
        printer.print(message);    
    }

}

آمن MessagePrinterInternal.java - هذا الفصل يعتمد على فئة API. كما يوحي الاسم، فهو مخصص للاستخدام "الداخلي" في مكان آخر في إطاري الصغير.

package impl;

import api.MessagePrinter;

/**
 * An "internal" class, not meant to be added to your
 * application classpath. Think the Tomcat Servlet API implementation classes.
 */
public class MessagePrinterInternal extends MessagePrinter {

    public void print(String message) {
        System.out.println("INTERNAL: " + message);
    }
}

أخيرا، الفئة الوحيدة في وحدة التطبيق ...MyApp.java

import api.MessageHolder;
import api.MessagePrinter;

public class MyApp {

    public static void main(String[] args) {
        MessageHolder holder = new MessageHolder("Hope this compiles");
        holder.print(new MessagePrinter());
    }

}

لذلك، الآن أحاول تجميع طلبي الصغير، MyApp.java. لنفترض أن الجرار API يتم تصديرها عبر جرة، ويقول API.JAR، وكوني مواطنا جيدا أحيل فقط أن جرة في فصف فصف - وليس من فئة ما شحنة في impl.jar.

الآن، من الواضح أن هناك خللا في تصميم الإطار الخاص بي في أن فئات API لا ينبغي أن يكون لها أي اعتماد على فئات التنفيذ "الداخلية". ومع ذلك، ما جاء كمفاجأة هو أن MyApp.java لم يترجم على الإطلاق.

javac -cp api.jar src\MyApp.java
src\MyApp.java:11: cannot access impl.MessagePrinterInternal class file for impl.MessagePrinterInternal not found

    holder.print(new MessagePrinter());
                 ^
      1 error

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

لذلك، هل هذا علة Javac، أو بعض الغريب من JLS؟

مترجم: Sun Javac 1.6.0_14

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

المحلول

لا يوجد شيء خاطئ في JLS أو Javac. بالطبع هذا لا يترجم، لأن صفك MessageHolder المراجع MessagePrinterInternal وهو ليس على Compile Classpath إذا فهمت شرحك الصحيح. يجب عليك كسر هذا المرجع في التنفيذ، على سبيل المثال مع واجهة في API الخاص بك.

تحرير 1: للتوضيح: هذا لا علاقة له بالطريقة المرئية على الحزمة كما يبدو أنك تعتقد. المشكلة هي أن النوع MessagePrinterInternal هناك حاجة للتجميع، ولكن ليس لديك على ClassPath. لا يمكنك توقع جافاك ترجمة شفرة المصدر عندما لا يكون لها حق الوصول إلى الفصول المرجعية.

تحرير 2: أعد قراءة الكود مرة أخرى وهذا ما يبدو أن يحدث: عندما يتم تجميع MyApp، فإنه يحاول تحميل messopholder الفئة. مراجع Messageholder الفئة MessagePrinterInternal، لذلك يحاول تحميل ذلك أيضا وفشل. لست متأكدا من أنه محدد في JLS، قد يعتمد أيضا على JVM. في تجربتي مع Sun JVM، تحتاج إلى أن يكون لديك ما لا يقل عن جميع الفصول الدراسية المرجعية بشكل ثابت عند تحميل فئة؛ يتضمن ذلك أنواع الحقول، أي شيء في توقيع الأسلوب، والصلصات الموسعة واجهات منفذة. هل يمكن أن تجادل أن هذا هو سهل الاستخدام، لكنني أود أن أرد على أنه بشكل عام، فهناك القليل جدا أنت تفعل مع فئة مفقودة حيث تكون هذه المعلومات مفقودة: لا يمكنك إنشاء الكائنات، ولا يمكنك استخدام البيانات الوصفية (كائن الفصل) إلخ. مع هذه المعرفة الخلفية، أود أن أقول أن السلوك الذي تراه هو متوقع.

نصائح أخرى

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

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

يؤدي تغيير كل شيء في حزمة API إلى إصلاح مشكلتك وتعطيك فصل نظيف في التعليمات البرمجية الخاصة بك.

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

لا أعرف المواصفات الدقيقة حول هذه المسألة، وأشك في أن مؤلفي Javac قد ارتكبوا أي خطأ هنا. لكن من يهتم؟ لماذا لا تضع جميع التبعيات في Classpath، حتى لو كانت بعضها سطحي؟ القيام بذلك يجعل باستمرار لنا يعيش أسهل بكثير.

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