java - Kotlin 函数参数:如何定义一个可以有尾随 lambda 或接口作为参数的函数?
问题描述
我发现了两个类似的代码:
binding.playButton.setOnClickListener (
Navigation.createNavigateOnClickListener(R.id.action_titleFragment_to_gameFragment)
)
binding.playButton.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_titleFragment_to_gameFragment)
}
来自android视图类的Java代码:
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
问题是:如何创建可以使用尾随 lambda 或接口作为参数的函数?我得到类型不匹配。
interface One {
fun a(): Int
}
class OneImp : One {
override fun a(): Int {
return 4
}
}
fun test(one: One) {
val a = one
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val a = OneImp()
test (a) //works fine
test {
a //error
}
}
错误:
Type mismatch.
Required:
TitleFragment.One
Found:
() → TitleFragment.OneImp
更新:
在@Jenea Vranceanu 的回答之后,我在测试SAM 时发现了我的错误(我使用了kotlin 文件中的接口,而所有代码都应该在java 中)。解决方案将是:(在 kotlinv v1.4 版本之前)创建一个 java 文件:
public class Mine {
public interface One {
int a();
}
public class OneImpl implements One {
@Override
public int a() {
return 4;
}
}
public void test(One one) {}
}
然后我可以同时使用函数参数和 lambda。现在在 kotlin 文件中:
Mine().test {4}
val b = Mine().OneImpl()
Mine().test (b)
PS。如果他将其添加到他的答案中,我将从此处删除。
解决方案
您误解了binding.playButton.setOnClickListener
每种情况下的工作方式。
在第一个中,Navigation
组件创建View.OnClickListener
传递到 setOnClickListener
(注意括号或圆括号):
binding.playButton.setOnClickListener (
Navigation.createNavigateOnClickListener(R.id.action_titleFragment_to_gameFragment)
)
在 Java 中,它看起来几乎相同:
binding.getPlayButton().setOnClickListener (
Navigation.createNavigateOnClickListener(R.id.action_titleFragment_to_gameFragment)
)
第二个示例的工作方式略有不同。首先,您View.OnClickListener
使用大括号创建并定义此View.OnClickListener
方法的主体onClick(View view)
:
binding.playButton.setOnClickListener { /* empty body of onClick(View view) */ }
您参考了大括号内的单击视图:
binding.playButton.setOnClickListener {
it.context // `it` is the clicked view
}
// It is the same as
binding.playButton.setOnClickListener { view ->
view.context
}
为什么它不能完全工作?
这种定义匿名类的类型只有在 Java 中定义的 SAM 类才有可能,并且View.OnClickListener
是在 Java 中定义的 SAM 类。Kotlin SAM 类/接口尚不支持此功能。
通过写作:
val a = OneImp()
test {
a //error
}
您假设test
函数声明如下所示:
fun test(one: () -> One) {
val a = one()
}
请注意,在第二个示例中,您的问题顶部Navigation.findNavController(it).navigate
没有创建View.OnClickListener
对象。大括号创建了这个对象,由于这个接口只有一个抽象方法,所以没有必要编写这个方法签名,所以大括号内声明的所有内容都直接进入void onClick(View view)
方法。
更新(官方文档)
Kotlin 中的 SAM 转换。这是官方文档,底部写着:
...请注意,此功能仅适用于 Java 互操作;由于 Kotlin 具有适当的函数类型,因此不需要将函数自动转换为 Kotlin 接口的实现,因此不受支持。
所以 Kotlin 接口永远不会支持它,因为不需要它。看来我对“永远不会支持SAM”是错误的。它将是,但它尚不可用。Kotlin 类的 SAM 转换将从 Kotlin 1.4 开始提供,该版本现在是一个候选版本。
更新解决方案
最初没有添加替代解决方案。发问者礼貌地要求添加下一个代码以使答案完整。谢谢!
public class Mine {
public interface One {
int a();
}
public class OneImpl implements One {
@Override public int a() { return 4; }
}
public void test(One one) {}
}
推荐阅读
- python - 带有脏数据的特征工程
- javascript - 如何通过ajax将xml文件数据发送到Webmethod?
- iis - IIS 10 URL 将 http 流量重写为 https 重定向不起作用
- javascript - 在“动态”画布上绘图
- arduino - ESP32 上的 AsyncTCP 和带有 SOFTAP 的奇怪堆/套接字问题
- python - 为什么 json 似乎无法正确处理具有相当大数据的对象
- django - 不需要外键
- r - 排名与并列数字
- css - 带有 CSS 的 tabsetPanel 样式(不止一种样式),R Shiny
- powershell - 使用 Powershell 恢复 SSRS/PowerBI 报告服务器加密密钥