
在java开发中,我们经常会遇到多个类具有相似的属性、方法和初始化逻辑。以android ui组件为例,不同的视图元素(如加载动画、错误提示)可能都需要与特定的 viewdatabinding 实例关联,并进行生命周期所有者的设置。这种情况下,将公共的初始化代码进行分组,以提高代码复用性和可维护性是自然而然的需求。
考虑以下两个具有类似初始化逻辑的类:
public class LoadElement {
LoadingElementBinding binding;
public LoadElement(ViewGroup parent) {
binding = LoadingElementBinding.inflate(
LayoutInflater.from(parent.getContext()),
parent,
false));
binding.setLifecycleOwner(ViewTreeLifecycleOwner.get(parent));
}
public void doDomething() {
// 与binding相关的操作
}
}
public class ErrorElement {
ErrorElementBinding binding;
public ErrorElement(ViewGroup parent) {
binding = ErrorElementBinding.inflate(
LayoutInflater.from(parent.getContext()),
parent,
false));
binding.setLifecycleOwner(ViewTreeLifecycleOwner.get(parent));
}
public void doDomething() {
// 与binding相关的操作
}
}可以看到,binding.setLifecycleOwner(...) 这行代码是重复的,而 binding 的创建方式(LoadingElementBinding.inflate vs ErrorElementBinding.inflate)是不同的。为了避免重复,我们可能会尝试使用抽象父类来封装公共逻辑。
一种直观的解决方案是创建一个抽象父类 BindingElement,将公共的 setLifecycleOwner 逻辑放在其构造器中,并将差异化的 inflate 逻辑抽象为一个方法,由子类实现。
public abstract class BindingElement <T extends ViewDataBinding>{
T binding;
public BindingElement (ViewGroup parent) {
// 尝试在构造器中调用抽象方法
binding = createBinding(LayoutInflater.from(parent.getContext()), parent);
binding.setLifecycleOwner(ViewTreeLifecycleOwner.get(parent));
}
// 抽象方法,期望子类实现
abstract T createBinding(LayoutInflater inflater, ViewGroup parent);
public void doDomething() {
// 与binding相关的操作
}
}
public class LoadElement extends BindingElement<LoadingElementBinding>{
public LoadElement(ViewGroup parent) {
super(parent); // 调用父类构造器
}
@Override
LoadingElementBinding createBinding(LayoutInflater inflater, ViewGroup parent){
return LoadingElementBinding.inflate(inflater, parent, false);
}
}
// ErrorElement 类似 LoadElement然而,这种做法存在一个经典的Java构造器陷阱:在父类构造器中调用非 final 或抽象方法是危险的。 当 BindingElement 的构造器被调用时,子类(如 LoadElement)的构造器尚未完全执行,其 createBinding 方法可能尚未被正确初始化或覆盖。此时调用 createBinding,可能会导致意外的行为,甚至 NullPointerException。因为 binding 字段的赋值依赖于子类的实现,在父类构造器执行时,子类的状态可能不完整。
立即学习“Java免费学习笔记(深入)”;
为了解决上述问题,我们可以利用Java 8引入的函数式接口和Lambda表达式(或方法引用)来将具体的 binding 创建逻辑作为参数传递给父类构造器。这样,父类构造器在执行时就能直接获得子类提供的具体行为,而无需调用未完全初始化的抽象方法。
首先,定义一个函数式接口 BindingCreator,它封装了 inflate 方法的签名。
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewTreeLifecycleOwner;
@FunctionalInterface
public interface BindingCreator<T extends ViewDataBinding>{
/**
* 创建并返回一个ViewDataBinding实例。
* @param inflater 用于膨胀布局的LayoutInflater。
* @param parent 布局的父ViewGroup。
* @param attachToParent 是否将膨胀的视图附加到父视图。
* @return 创建的ViewDataBinding实例。
*/
T createBinding(LayoutInflater inflater, ViewGroup parent, boolean attachToParent);
}@FunctionalInterface 注解是可选的,但它明确了这是一个函数式接口,并允许编译器检查其是否只有一个抽象方法。
接着,修改 BindingElement 抽象类,使其构造器接受一个 BindingCreator 实例。
public abstract class BindingElement <T extends ViewDataBinding>{
protected T binding; // 将binding声明为protected,方便子类访问
public BindingElement(ViewGroup parent, BindingCreator<T> bindingCreator){
// 通过传入的bindingCreator来创建binding
binding = bindingCreator.createBinding(
LayoutInflater.from(parent.getContext()),
parent,
false));
binding.setLifecycleOwner(ViewTreeLifecycleOwner.get(parent));
}
public void doDomething() {
// 与binding相关的操作
}
}现在,BindingElement 的构造器不再直接调用抽象方法,而是通过 bindingCreator 这个参数来执行具体的创建逻辑。
最后,子类 LoadElement 和 ErrorElement 在调用父类构造器时,通过方法引用传递它们的 inflate 方法。
import com.example.app.LoadingElementBinding; // 假设这是你的生成类
import com.example.app.ErrorElementBinding; // 假设这是你的生成类
public class LoadElement extends BindingElement<LoadingElementBinding>{
public LoadElement(ViewGroup parent) {
// 使用方法引用 LoadingElementBinding::inflate 作为 BindingCreator 的实现
super(parent, LoadingElementBinding::inflate);
}
// 其他特有方法
}
public class ErrorElement extends BindingElement<ErrorElementBinding>{
public ErrorElement(ViewGroup parent) {
// 使用方法引用 ErrorElementBinding::inflate 作为 BindingCreator 的实现
super(parent, ErrorElementBinding::inflate);
}
// 其他特有方法
}在这里,LoadingElementBinding::inflate 是一个方法引用,它等价于一个Lambda表达式 (inflater, parent, attachToParent) -> LoadingElementBinding.inflate(inflater, parent, attachToParent)。这个方法引用被编译器自动转换为 BindingCreator 接口的一个匿名实现,并作为参数传递给 BindingElement 的构造器。
注意事项:
通过巧妙地结合抽象类和Java 8的函数式接口(Lambda表达式或方法引用),我们能够优雅地解决在父类构造器中调用抽象方法所带来的问题。这种模式不仅提高了代码的复用性和安全性,还使得类结构更加清晰,符合现代Java编程的最佳实践。在处理具有相似初始化逻辑的类时,优先考虑使用这种行为参数化的方式,可以有效避免常见的构造器陷阱,并写出更健壮、更易维护的代码。
以上就是Java中优雅地分组初始化代码:利用函数式接口解决构造器抽象方法调用问题的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号