Я вижу поведение загрузки класса, которое, по-видимому, несовместимо с спецификацией JVM, и я задаюсь вопросом, является ли это ошибкой. Или если нет, надеясь, что кто-то сможет объяснить, почему.
Приведенный ниже примерный код просто печатает привет из основного метода. У него есть неиспользуемый метод, который содержит вызов метода для метода, который объявляет, что он принимает в качестве аргумента "C" (который является интерфейсом).
Когда main выполняется (без A, B и C в пути к классу) для интерфейса C. для класса C выбрана ошибка ClassNotFound. (Примечание C никогда не требуется на время выполнения, поскольку оно ссылается на метод, который никогда не выполняется).
Это, по-видимому, является нарушением спецификации JVM
Раздел 2.17.1 Java VM Spec, второе издание гласит:
Единственное требование, касающееся того, когда выполняется разрешение, заключается в том, что любые ошибки, обнаруженные во время разрешения, должны быть выброшены в точку в программе, где программа выполняет какое-либо действие, которое может прямо или косвенно требовать привязки к классу или интерфейс, участвующий в ошибке
Раздел 2.17.3 Java VM Spec, второе издание гласит:
Язык программирования Java позволяет гибкость реализации при связывании действий (и из-за рекурсии, загрузки), при условии соблюдения семантики языка, что класс или интерфейс полностью проверены и подготовлены до него инициализируется и что ошибки, обнаруженные во время связывания, бросаются в точку в программе, где некоторые действия выполняются программой, которая может потребовать привязки к классу или интерфейсу, участвующим в ошибке.
Примечание. Если я изменю тип параметра в определении для класса вместо интерфейса, тогда код загружается и выполняется правильно.
/**
* This version fails, the method call in neverCalled() is to a method whose
* parameter definition is for an Interface
*/
public class Main {
public void neverCalled(){
A a = new A();
B b = new B(); // B implements C
//method takeInter is declared to take paramters of type Interface C
//This code is causes a ClassNotFound error do be thrown when Main
//is loaded if A, B, and C is not in the class path
a.takeInter(b);
}
public static void main(String[] args) {
System.out.println("Hello...");
}
}
/**
* This version runs, the method call in neverCalled() is to a method whose
* parameter definition is for a Class
*/
public class Main {
public void neverCalled(){
A a = new A();
B b = new B(); // B implements C
//method takeInter is declared to take paramters of type Interface C
//This code is causes a ClassNotFound error do be thrown when Main
//is loaded if A, B, and C is not in the class path
a.takeClass(b);
}
public static void main(String[] args) {
System.out.println("Hello...");
}
}
public class A {
public void takeClass(B in){};
public void takeInter(C in){}
}
public class B implements C {}
public interface C {}
Ed,
Я не намеренно пытался вывести цитату из контекста, я вытащил то, что я считал подходящей частью. Спасибо, что помогли мне понять это.
Во всяком случае, спецификация кажется мне совершенно понятной. В нем говорится, что ошибки должны быть выбраны в, а не точкой. Конечно, я прочитал спецификацию VM после прочтения следующего в главе 8 Inside Virtual Java, поэтому, возможно, это окрасило мою интерпретацию.
От, http://www.artima.com/insidejvm/ed2/linkmod.html
Как описано в главе 7 "Время жизни класса", различным реализациям виртуальной машины Java разрешено выполнять разрешение в разное время во время выполнения программы. Реализация может выбрать, чтобы связать все по фронту, следуя всем символическим ссылкам из исходного класса, затем все символические ссылки из последующих классов, пока каждая символическая ссылка не будет разрешена. В этом случае приложение будет полностью связано до того, как будет вызван метод main(). Такой подход называется ранним разрешением. В качестве альтернативы, реализация может решить подождать до последней минуты, чтобы разрешить каждую символическую ссылку. В этом случае виртуальная машина Java разрешит символическую ссылку только тогда, когда она будет сначала использоваться запущенной программой. Этот подход называется поздним разрешением. Реализации могут также использовать стратегию разрешения между этими двумя крайностями.
Несмотря на то, что реализация виртуальной машины Java имеет определенную свободу выбора, когда нужно разрешать символические ссылки, каждая виртуальная машина Java должна давать внешнее впечатление о том, что использует позднее разрешение. Независимо от того, когда конкретная виртуальная машина Java выполняет свое разрешение, , она всегда будет вызывать любую ошибку, которая возникает при попытке разрешить символическую ссылку в точке выполнения программы, где символическая ссылка была фактически использована в первый раз. Таким образом, пользователь всегда будет выглядеть так, как будто разрешение было запоздалым. Если виртуальная машина Java делает раннее разрешение, и во время раннего разрешения обнаруживает, что файл класса отсутствует, он не будет сообщать о файле класса, отсутствующем, бросая соответствующую ошибку до тех пор, пока в программе не будет использоваться что-то в этом файле класса. Если класс никогда не используется программой, ошибка никогда не будет выбрана.