Java代码编译成功但生成IllegalAccessError错误

3
在下面的代码中,我提供了两个主要类 - TestWorks 和 TestCompilesButFails。我不确定我理解这个失败的原因 - 似乎 Arrays.asList() 表达式被给定类型“List of AbstractBaseClass”,但为什么在这里给出引用另一个包中的包本地类的类型是正确的呢?
// failing test class
import somepackage.*;
import java.util.Arrays;

public class TestCompilesButFails {
    public static void main(String [] args){
        // fails here with java.lang.IllegalAccessError: 
        // tried to access class somepackage.AbstractBaseClass 
        // from class TestCompilesButFails
        for (Object o : Arrays.asList(new ConcreteA(), new ConcreteB())) { 
            System.out.println(o);
        }
    }
}


// package-local abstract base class
package somepackage;

abstract class AbstractBaseClass {
    public abstract void doSomething();
}

// next two classes - public extenders of abstract base class
package somepackage;

public class ConcreteA extends AbstractBaseClass {
    public void doSomething(){
        System.out.print("Look, ma!\n");
    }
}

package somepackage;

public class ConcreteB extends AbstractBaseClass {
    public void doSomething(){
        System.out.print("No types!\n");
    }
}

// working test 
import somepackage.*;

public class TestWorks {
    public static void main(String [] args){
        new ConcreteA().doSomething();
        new ConcreteB().doSomething();
    }
}

只是为了排除另一个因素 - 用私有静态<T> List<T> myAsList(T first, T second)替换Arrays.asList(),会出现相同的问题 - 这与Arrays.asList()中的可变参数无关。 - nerdytenor
2个回答

3

由于Java语言规范中指定的类型推断算法未考虑类型可见性:

超类型约束T :> X意味着解决方案是X的超类型之一。给定对T的几个这样的约束,我们可以交集每个约束所暗示的超类型集,因为类型参数必须是它们所有成员之一。然后我们可以选择在交集中最具体的类型。

至于为什么他们以那种方式定义它,我猜想这是为了避免使已经相当复杂的算法变得更加复杂,唯一的目的是处理罕见的边角情况。毕竟,他们还写道:

还要注意的是,类型推断不会以任何方式影响完整性。如果推导出的类型是荒谬的,则调用将产生类型错误。类型推断算法应被视为一种启发式算法,旨在实践中表现良好。如果无法推断出所需结果,则可以使用显式类型参数。

在你的情况下,这将是:

    for (Object o : Arrays.<Object>asList(new ConcreteA(), new ConcreteB())) { 
        System.out.println(o);
    }

感谢您在Java规范中找到相关部分 - 我怀疑它只是查找最“特定”的公共祖先,而不考虑可见性。 - nerdytenor
我仍然不确定在实践中为什么指定最具体的可见祖先会更加复杂...我需要再考虑一下。 - nerdytenor

0

认为你指的是Java的“包保护”分类;一个未声明为public、private或protected的方法或变量因此成为“包保护”,因此可以被该包中的任何类访问。它并没有被广泛使用(谢天谢地),我不认为这是一个好的默认设置,但自Java 1.0以来就一直保持这种方式。


我修改了我的问题,以更明确地引用包局部变量。然而,我的问题仍然存在。 - nerdytenor
顺便说一下,我发现包局部类非常有用。 - nerdytenor
尽可能避免使用包级作用域,除非真的必要。它会影响可重用性。 - Steven
@Steven 是的 - 包范围在你不想要可重用性,而是想要在类级别上隐藏信息时非常有用。 - nerdytenor
我经常在我的一个中等规模的项目(约20k行代码)中创建包本地类。在编写API时,通常有必要编写帮助类以供内部使用,而不将它们暴露给外部世界。我不确定这与“黑客代码”有什么关系... - nerdytenor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接