Вопрос

When I run the source code below, the runtime error that I see in the shell window is as follows:

Exception in thread "main" java.lang.IllegalAccessError: tried to access method
pkgs.test.B.mStaPro()V from class pkgs.main.Main3
        at pkgs.main.Main3.m6(Main3.java:919)
        at pkgs.main.Main3.main(Main3.java:9)

What does the letter V mean, seen after the mStaPro() above?

Here is my source code, which all compiles:

class Main3, package main:

package pkgs.main;
import pkgs.test.B;

class Main3 {
    static public void main(String args[]) {
        new Main3().m6();
    }

    void m6() {
        B.mStaPro();
    }
}

class A, package main:

package pkgs.main;

public class A {
    static protected void mStaPro() { System.out.println("A mStaPro()"); }
}

class B, package test:

package pkgs.test;
import pkgs.main.A;

public class B extends A {
    // Note:  if this line below is commented out, then the runtime exception
    // mentioned in this post's title is not seen.
    static protected void mStaPro() { System.out.println("B mStaPro()"); }
}

Here are the contents of my shell-based compile and run batch files:

REM For compilation:
javac -Xlint -sourcepath ..\src -d ..\cls ..\src\pkgs\main\Main3.java


REM For running:
java -cp ..\cls pkgs.main.Main3

Please note the comment that I made inside class B. Thanks a lot for any comments.

EDIT:

I tried building my source code using Apache Ant, but the result obtained is identical:

run:
     [java] Exception in thread "main" java.lang.IllegalAccessError: tried to access method pkgs.test.B.mStaPro()V from class pkgs.main.Main3
     [java]     at pkgs.main.Main3.m6(Main3.java:11)
     [java]     at pkgs.main.Main3.main(Main3.java:7)
     [java] Java Result: 1

main:

BUILD SUCCESSFUL

Please note that in the first exception error message at the top of this post, I had a lot of code commented out in the source code, hence the line numbers differ between that error message, and Ant's error message immediately above.

One other thing that I have tried is upgrading my JDK from version 1.7.0_40, to version 1.7.0_55.

EDIT 2:

Here is my Apache Ant build.xml file. It's almost identical to the tutorial build.xml file provided on the Apache Ant website:

<project name="Main3 test" basedir="." default="main">

    <property name="src.dir"     value="src"/>

    <property name="build.dir"   value="build"/>
    <property name="classes.dir" value="${build.dir}/classes"/>
    <property name="jar.dir"     value="${build.dir}/jar"/>

    <property name="main-class"  value="pkgs.main.Main3"/>



    <target name="clean">
        <delete dir="${build.dir}"/>
    </target>

    <target name="compile">
        <mkdir dir="${classes.dir}"/>
        <javac srcdir="${src.dir}" destdir="${classes.dir}" 
                includeantruntime="false" debug="true" 
        debuglevel="lines,vars,source" />
    </target>

    <target name="jar" depends="compile">
        <mkdir dir="${jar.dir}"/>
        <jar destfile="${jar.dir}/${ant.project.name}.jar" 
         basedir="${classes.dir}">
            <manifest>
                <attribute name="Main-Class" value="${main-class}"/>
            </manifest>
        </jar>
    </target>

    <target name="run" depends="jar">
        <java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
    </target>

    <target name="clean-build" depends="clean,jar"/>

    <target name="main" depends="clean,run"/>

</project>
Это было полезно?

Решение

A Java class is normally validated at three stages (roughly speaking):

  1. At its compilation - this (obviously) happens when you compile a Java class.
  2. When it is loaded into the Java Virtual Machine - this happens when you first reference a class.
  3. During its actual execution - this happens the latest when you first use a specific code segment.

Most programming errors are caught by the Java compiler. However, the Java compiler is tricked by your method shadowing (hiding) of the pkgs.main.A#mStaPro method by the pkgs.test.B#mStaPro method. Here is what happens:

When you compile your Main class, you are invoking the static method mStaPro on B. The question is, are you invoking the shadowed method that is defined in the pkgs.main.A class or the hiding method that is defined in the pkgs.test.B class? In this question, the Java compiler and the Java runtime come to different conclusions which is why the Java compiler approves your code while the Java runtime refuses it:

  1. The Java compiler considers your code legal because it thinks that you are invoking the mStaPro method that is defined in A. The A class lives in the same package as Main such that its protected methods are considered to be visible to the Main class.
  2. The Java runtime considers your code illegal because it thinks that you are invoking the mStaPro method that is defined in B. This method is also protected but it is defined in the pkgs.test package. Therefore, this mStaPro must not be invoked from Main which lives in a different package.

In order to inform you about this illegal code, the Java runtime throws you the IllegalAccessError that you encounter. Let us just go one step deeper here. If you look at the generated Java byte code, the Main#m method is compiled as follows:

invokestatic  #5  // Method pkgs/test/B.mStaPro:()V
return        

what resembles your Java source code:

B.mStaPro()
return           // a void return statement is implicit in Java source code

This compilation result is independent on whether the B class defines a method mStaPro or not. And this is the cause of the exception you encounter. In cases that the B class defines a method mStaPro, the invokestatic invocation is bound to the B#mStaPro method (what is illegal). Otherwise, the invocation is bound to A#mStaPro (what is legal).

To solve this problem, you should name your actual target class A.mStaPro() instead of invoking the method on B. However, I must honestly say that I find the behavior of the Java compiler counter-intuitive. A static method is, well, static and not dynamic. Therefore, the Java compiler should statically bind the invocation of B.mStaPro() to pkgs/main/A.mStaPro:()V at compile time. Effectively, without this assumption, the compilation could not be successful. And even better, it should simply produce a compilation error when it encounters that an inaccessible B is shadowing the target in A and that the code cannot run successfully.

Finally, a minor hint on this behavior might be given in Oracle's tutorial on static invocation which mentions this behavior:

The version of the hidden static method that gets invoked depends on whether it is invoked from the superclass or the subclass.

And yet, this is not good enough for the reasons I mentioned above. However, I worked with Java in many years and since shadowing is not a recommended practice, I never encountered this problem. Putting it like this, you really found a borderline case.

Info: I posted this to the Java compiler mailing list. Once I get an answer, I will add the information to this posting.

Другие советы

Change this line

static protected void mStaPro() { System.out.println("B mStaPro()"); }

to

 static public void mStaPro() { System.out.println("B mStaPro()"); }

javadocs-Controlling Access to Members of a Class

At the member level, you can also use the public modifier or no modifier (package-private) just as with top-level classes, and with the same meaning. For members, there are two additional access modifiers: private and protected. The private modifier specifies that the member can only be accessed in its own class. The protected modifier specifies that the member can only be accessed within its own package (as with package-private) and, in addition, by a subclass of its class in another package.

your class A and B are not in same package, and the function B.mStaPro() is protected, it can not be accessed from other package, you should put them in same package or make the function as public.

You can't able to access protected methods of one package in a class(i.e. the classes other than subclasses) of other package.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top