MST

星途 面试题库

面试题:Kotlin函数式接口与SAM转换在复杂场景下的应用

在一个安卓应用开发场景中,你需要实现一个自定义的视图 `CustomView`,它需要支持多种不同类型的用户交互,每种交互都用函数式接口定义,如 `OnDragListener`、`OnLongPressListener` 等。请阐述如何使用SAM转换来处理这些复杂的用户交互逻辑,同时说明在处理多个函数式接口时可能遇到的问题及解决方案。
25.8万 热度难度
编程语言Kotlin

知识考点

AI 面试

面试题答案

一键面试

1. 使用 SAM 转换处理用户交互逻辑

  • 定义函数式接口: 已经给定了如 OnDragListenerOnLongPressListener 等函数式接口,这些接口只有一个抽象方法。例如,假设 OnDragListener 定义如下:
    @FunctionalInterface
    public interface OnDragListener {
        void onDrag(int x, int y);
    }
    
  • CustomView 中使用: 在 CustomView 类中,可以定义方法接收这些函数式接口的实例。比如:
    public class CustomView extends View {
        private OnDragListener onDragListener;
        private OnLongPressListener onLongPressListener;
    
        public void setOnDragListener(OnDragListener listener) {
            this.onDragListener = listener;
        }
    
        public void setOnLongPressListener(OnLongPressListener listener) {
            this.onLongPressListener = listener;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                long startTime = System.currentTimeMillis();
                // 模拟长按检测
                new Handler().postDelayed(() -> {
                    if (System.currentTimeMillis() - startTime > 500 && onLongPressListener != null) {
                        onLongPressListener.onLongPress();
                    }
                }, 500);
            } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                if (onDragListener != null) {
                    onDragListener.onDrag((int) event.getX(), (int) event.getY());
                }
            }
            return true;
        }
    }
    
    onTouchEvent 方法中,根据不同的触摸事件类型,调用相应函数式接口的方法。这里使用了 SAM 转换,当我们为 CustomView 设置监听器时,可以直接使用 lambda 表达式。例如:
    CustomView customView = findViewById(R.id.custom_view);
    customView.setOnDragListener((x, y) -> {
        // 处理拖动逻辑
        Log.d("CustomView", "Dragging to x: " + x + ", y: " + y);
    });
    customView.setOnLongPressListener(() -> {
        // 处理长按逻辑
        Log.d("CustomView", "Long pressed");
    });
    

2. 处理多个函数式接口时可能遇到的问题及解决方案

  • 问题
    • 混淆问题:当有多个相似的函数式接口时,可能会混淆它们的方法。例如,如果有 OnClickOnceListenerOnClickTwiceListener,可能会不小心在错误的地方使用错误的接口。
    • 命名冲突:如果在不同的库或模块中定义了同名的函数式接口,可能会导致编译错误。
    • 代码可读性:随着函数式接口数量的增加,CustomView 类中的代码可能变得复杂,难以理解和维护。
  • 解决方案
    • 清晰命名:给函数式接口起清晰、有意义的名字,避免命名冲突和混淆。例如,OnSingleTapListenerOnDoubleTapListener 就比简单的 OnTapListener 更清晰。
    • 使用注释:在函数式接口和使用它们的地方添加详细的注释,说明每个接口的用途和期望的行为。例如:
    /**
     * 用于处理用户在 CustomView 上的长按操作
     */
    @FunctionalInterface
    public interface OnLongPressListener {
        void onLongPress();
    }
    
    • 模块化代码:将与不同函数式接口相关的逻辑分离到不同的方法或类中,提高代码的可读性和可维护性。例如,可以将与拖动相关的逻辑封装到一个 DragHandler 类中,将长按相关的逻辑封装到 LongPressHandler 类中。
    public class DragHandler {
        private OnDragListener onDragListener;
    
        public DragHandler(OnDragListener listener) {
            this.onDragListener = listener;
        }
    
        public void handleDrag(int x, int y) {
            if (onDragListener != null) {
                onDragListener.onDrag(x, y);
            }
        }
    }
    
    然后在 CustomView 中:
    public class CustomView extends View {
        private DragHandler dragHandler;
        private LongPressHandler longPressHandler;
    
        public CustomView(Context context, AttributeSet attrs) {
            super(context, attrs);
            dragHandler = new DragHandler(null);
            longPressHandler = new LongPressHandler(null);
        }
    
        public void setOnDragListener(OnDragListener listener) {
            dragHandler = new DragHandler(listener);
        }
    
        public void setOnLongPressListener(OnLongPressListener listener) {
            longPressHandler = new LongPressHandler(listener);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                longPressHandler.startLongPressCheck(event.getDownTime());
            } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
                dragHandler.handleDrag((int) event.getX(), (int) event.getY());
            }
            return true;
        }
    }