添加了新的(部分重构的)代码,现在获取从未存在的 class 的 ClassNotFoundException

Added new (partially refactored) code, now getting ClassNotFoundException for a class that never existed

我的应用程序 activity 中有以下代码:

package com.myself.foo.myapp;

import com.someotherfellow.hisapp.OtherClass;
// more imports here

public class MainActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Object bar = new OtherClass(blah, this, YetAnotherClass.class);
        // ...
    }
}

当我构建并启动它时(使用 Eclipse 和 ADT),应用程序崩溃并出现未处理的 ClassNotFoundException。 logcat 告诉我缺少的 class 是

com.myself.foo.myapp.OtherClass

——这是一个不存在的class。简单的 class 名称是来自另一个包的 class 的名称,但它以我的包名称为前缀。显然 class 将永远找不到。

当我将鼠标悬停在我的代码中 OtherClass 的构造函数调用上时,Eclipse 显示正确的 class 名称和正确的包前缀。

这两个包都存在于与源文件相同的源树中——各自的路径是:

~/src/myapp/src/com/myself/foo/myapp/MainActivity.java
~/src/myapp/src/com/someotherfellow/hisapp/OtherClass.java

是什么导致了这种行为,我该如何解决?


编辑: 出于好奇,我重构了代码,将 OtherClass 移动到 Android 试图找到它的包中。现在我得到:

java.lang.InstantiationException: java.lang.Class<com.myself.foo.myapp.OtherClass> has no zero argument constructor

确实如此——OtherClass 的构造函数采用调用中提供的三个参数。

由于某种原因,JVM 似乎正在调用 new OtherClass()(参数为零),它与 class 的任何已知构造函数都不匹配。如果 OtherClass 驻留在不同的包中,JVM 会尝试在当前包中查找名为 OtherClass 且具有匹配构造函数签名的 class。但是,如果 OtherClass 在同一个包中,JVM 至少会选择正确的 class,但显然找不到匹配的构造函数。

这引出了一个问题:是什么导致 JVM 寻找零参数构造函数?

请注意,OtherClass 不是 Android 组件:它不是来自属于 Android 框架的任何 classes,也没有被引用清单中的任何位置。 OtherClassObject 的直系后代并实现了自定义接口。

我相信我找到了错误。在我导入新代码之前,该应用程序已经运行。

我导入的代码是一个混合了 UI 和主要 activity 功能的应用程序。 OtherClass 是 activity 的精简版,其中我只保留了功能。

OtherClass原来叫MainActivity和我的主activity一样。我改名为OtherClass ] 在 Eclipse 中使用重构功能。

令我惊讶的是,我在清单中发现了以下内容:

    <activity
        android:name=".OtherClass"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

显然,两个相同的 class 名称(包名称除外)与本地引用一起混淆了 Eclipse,它更改了 Manifest 中的名称(它应该保持不变) .这就解释了为什么:

  • 关于 com.myself.foo.myapp.OtherClass 丢失的原始消息是因为根据其清单,此处的包是应用程序的默认包。
  • 在移动 OtherClass 以确保找到它之后,Android 仍然期望这个 class 是一个 Activity,它(像其他 Android系统组件)应该有一个零参数构造函数。

一旦我将清单中 Activity 的名称改回 MainActivity,错误就消失了。

经验教训: 当不同包中的两个 class 具有相同的简单名称时 有对的本地引用这些 classes,重命名其中之一可能会混淆 Eclipse。

建议:重构前提交所有内容。重构后,查看差异并查看更改是否正确。这可能会为您节省数小时的调试时间!