java - Increase visibility of interface
问题描述
I have two packages:
1. com.test.hidden
:
Contains an protected interface MyListener
and a public class FancyClass
with a public method for registering MyListener
objects:
package com.test.hidden;
public class FancyClass {
public void registerListener(MyListener listener) {
// ...
}
}
package com.test.hidden;
interface MyListener {
void doSomething();
}
2. com.test.myapp
:
Here I have an instance of FancyClass
and I want to register a new MyListener
object to it.
Obviously this will not work since MyListener
is not visible in com.test.myapp
. My intuition was to create a new interface in com.test.hidden
that extends MyListener
and is public:
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() {
// ...
}
})
}
}
But I get an 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)
When I change the visibility of MyListener
to public it works just fine, so I suspect Java does not like increasing the visibility of interfaces in subinterfaces. Why doesn't this work and how to solve this?
解决方案
Kotlin 不喜欢在公共方法中公开内部 API,如果这样做,实际上会导致编译器错误。如果您从 Kotlin 调用代码,这也会影响 Java 代码,但它会在运行时而不是在编译时引发异常。
至于解决方案,Kotlin 有internal
,但这是一个非常特殊的关键字:与 Java 的包 private (没有修饰符)不同, internal 使它在模块中的任何地方都可见。
这也是一种解决方案:转换为 Kotlin 并使用internal
. 如果你正在制作一个库,它在模块之外是不可见的,但你至少可以在内部声明它。
第二个是将接口转换为类并使用sealed class
. 请注意,这需要在同一个文件中声明所有子项。有关密封类的更多信息,请参阅此内容。
最后的解决方案:公开。我强烈建议您使用它而不是其他替代方案,主要是因为它是最简单的选择,并且不需要骇人听闻的设计。此外,如果您打算在其他地方访问接口,则必须使用特定的实现而不是通用超类,这并不总是可行的(尽管取决于您的使用)。
还有只使用 Java 的选项,但我假设你更喜欢混合,因为你已经在这样做了。但正如迈克尔所说,你真的应该避免这种设计:这是糟糕的设计。如果我是你,无论如何我都会公开。
推荐阅读
- jquery - 如何从 Jquery Tab Widget 调用 Jquery tabdrop() 插件?
- javascript - 在 Sequelize ORM 中只返回没有字段名的数据
- java - 为什么 Spark 不能在 Eclipse 上运行?
- javascript - 提交后关注特定的输入类型文本
- android - getStreamMaxVolume 上的 NullPointerException
- c++ - C++/Arduino flash LED 不同的间隔
- python - 我有一个存储在文本文件中的数据我想访问该数据但跳过初始值,一旦读取数据然后转到下一行
- python - 如何在一个命令中传递不敏感的标志以匹配?
- c# - RDCL 报告在 IIS 默认 80 端口中查看问题
- excel - 如果复制到新工作表后满足条件,如何清除单元格?