【问题标题】:Confused by Android access control mechanism. It's DIFFERENT from java被Android访问控制机制搞糊涂了。它与java不同
【发布时间】:2026-01-17 21:25:01
【问题描述】:

在java中,如果不在同一个包中,子类不能覆盖基类的默认或私有方法。 基类代码:

//base class
public class TestBase {
protected void test() {
    defaultTest();
    protectedTest();
    privateTest();
}

void defaultTest() {
    System.out.println("defaultTest");
}

protected void protectedTest() {
    System.out.println("protectedTest");
}

private void privateTest() {
    System.out.println("privateTest");
}
}

和子类代码:

public class TestImpl extends TestBase {
protected void test() {
    super.test();
    localTest();
}

private void localTest() {
    System.out.println("Impl:localTest");
}
protected void defaultTest() {
    System.out.println("Impl:defaultTest");
}

protected void protectedTest() {
    System.out.println("Impl:protectedTest");
}

public void privateTest() {
    System.out.println("Impl:privateTest");
}
public static void main(String[] args) {
    new TestImpl().test();
}
}

让我们将它们放入不同的包中,输出为:

defaultTest
Impl:protectedTest
privateTest
Impl:localTest

这反映了java中的访问控制机制。我们不能覆盖子类中的 defaultTest() 方法。但是在andorid中,我们可以! 在android.widget.AdapterView类中,有一个默认方法:

void checkSelectionChanged() {
    if ((mSelectedPosition != mOldSelectedPosition) || (mSelectedRowId != mOldSelectedRowId)) {
        selectionChanged();
        mOldSelectedPosition = mSelectedPosition;
        mOldSelectedRowId = mSelectedRowId;
    }
}

因此,同一包中的任何子类,例如 Spinner,都可以调用 checkSelectionChanged() 方法。然后调用 OnItemSelectedListener。我们可以添加自己的监听器来监听事件,我们会收到更改通知。 但是,如果我们扩展 Spinner,比如 CustomSpinner,并在 CustomSpinner 中定义相同的方法,代码如下:

public class CustomSpinner  extends Spinner{

public CustomSpinner(Context context, AttributeSet attrs, int defStyle,
        int mode) {
    super(context, attrs, defStyle, mode);
}

public CustomSpinner(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
}

public CustomSpinner(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public CustomSpinner(Context context, int mode) {
    super(context, mode);
}

public CustomSpinner(Context context) {
    super(context);
}

void checkSelectionChanged() {
    Log.i("CustomSpinner", "Called!");
}
}

然后奇怪的是 CustomSpinner 中的 checkSelectionChanged() 被调用并且我们的监听器永远不会被通知。这是由覆盖引起的。我错了吗?

【问题讨论】:

  • 我没有将其发布为答案,因为我不确定,但我认为重写方法意味着超类和子类方法的访问修饰符必须相同。在您的示例中并非如此。
  • 事实上,无论子类中定义哪个修饰符,我得到的结果都是一样的。
  • @gsingh2011 相同或更宽的访问修饰符...
  • 它根本不可能。 Google 只制作了自定义 JVM,而不是自定义语言规范。我不清楚为什么你认为 android 会执行不同的访问规则,但你能解释一下你认为在 android 中会发生什么和你的测试类吗?
  • @Thihara 我只是用我的 CustomSpinner 替换 android.widget.Spinner。我的方法 checkSelectionChanged() 在没有任何我的代码调用的情况下被调用。 Android Spinner 通过在内部调用 checkSelectionChanged() 来唤醒任何已注册的 OnItemSelectedListener。我认为不应该调用我的 checkSelectionChanged() 来影响 android 中的内部逻辑,因为在 java 中,我的 checkSelectionChanged() 不应该是覆盖代码,并且在我的 CustomSpinner 中只是本地的,正如 java 测试类所示

标签: java android access-control


【解决方案1】:

访问控制在 Android 和 Java 中完全一样。

Spinner 中没有onSelectionChanged() 方法——至少没有publicprotected。如果有这样的包私有方法,您可以覆盖它,但前提是您的类在同一个包中,而事实并非如此。您只是声明了一个具有相同名称的不相关方法;它不是压倒一切的。

这就是为什么你应该在你打算覆盖一个方法的地方使用@Override。它会产生一个编译错误,告诉你你实际上并没有覆盖。

【讨论】:

  • 其实我没有代码可以调用我的onSelectionChanged(),但是为什么它被调用并且android内部的onSelectionChanged()永远不会被调用?
  • 在 java 中,我知道它不是压倒一切的。所以我认为这是android中的一个愚蠢的功能。当我无意中定义与 android default 方法相同的方法时,这是很危险的。它会影响 android 内部逻辑,但我没有收到任何消息。