我有两个包裹:
1。 com.test.hidden
:
包含受受保护的接口MyListener
和公共类FancyClass
,并带有用于注册MyListener
对象的公共方法:
package com.test.hidden;
public class FancyClass {
public void registerListener(MyListener listener) {
// ...
}
}
package com.test.hidden;
interface MyListener {
void doSomething();
}
2。 com.test.myapp
:
这里我有一个FancyClass
的实例,我想为其注册一个新的MyListener
对象。
显然,这是行不通的,因为MyListener
在com.test.myapp
中不可见。我的直觉是在com.test.hidden
中创建一个扩展MyListener
并公开的新接口:
package com.test.hidden;
public interface MyListenerWrapper extends MyListener {
@Override
void doSomething();
}
package com.test.myapp
import com.test.hidden.FancyClass
import com.test.hidden.MyListenerWrapper
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val fc = FancyClass()
fc.registerListener(object : MyListenerWrapper {
override fun doSomething() {
// ...
}
})
}
}
但是我得到一个IllegalAccessError
:
java.lang.IllegalAccessError: Illegal class access: 'com.test.myapp.MainActivity' attempting to access 'com.test.hidden.MyListener' (declaration of 'com.test.myapp.MainActivity' appears in /data/app/com.test.myapp-ZzNlHhuBCCdAEV4QsZHspw==/base.apk)
at com.test.myapp.MainActivity.onCreate(MainActivity.kt:15)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1272)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2899)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3054)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1814)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:280)
at android.app.ActivityThread.main(ActivityThread.java:6710)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
当我将MyListener
的可见性更改为public时,它可以正常工作,因此我怀疑Java不喜欢增加子接口中接口的可见性。为什么这行不通?如何解决?
答案 0 :(得分:3)
Kotlin不喜欢在公共方法中公开内部API,如果这样做的话,实际上甚至会给编译器错误。如果您从Kotlin调用代码,这也会影响Java代码,但它会在运行时而不是在编译时引发异常。
对于解决方案,Kotlin具有internal
,但这是一个非常特殊的关键字:与Java的私有包(无修饰符)不同,内部使其在模块中的任何位置都可见。
这也是一种解决方案:转换为Kotlin并使用internal
。如果要创建库,则在模块外部将看不到该库,但是至少可以在内部对其进行声明。
第二个方法是将接口转换为类并使用sealed class
。请注意,这要求所有子项都在同一文件中声明。有关密封类的更多信息,请参见this。
最后的解决方案:将其公开。我高度建议您将此方法与其他替代方法结合使用,主要是因为它是最简单的选择,并且不需要hackish设计。另外,如果您打算访问其他地方的接口,则必须使用特定的实现,而不是一般的超类,但这并不总是可行的(尽管取决于您的使用)。
还有只使用Java的选项,但是我想您会喜欢混合,因为您已经在这样做了。但是作为Michael mentioned,您应该真正避免这种设计:这是糟糕的设计。如果我是你,我会以任何一种方式公开。