微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何习惯性地在Kotlin中编写Java的功能接口的默认方法?

如何解决如何习惯性地在Kotlin中编写Java的功能接口的默认方法?

我正在尝试惯用地转换Kotlin中称为Chain of Responsibility的设计模式的Java代码。但是我对在Kotlin中转换Java接口的方法appendNext()毫无头绪。我尝试了一些已经存在的问题,例如thisthis,但它们似乎不适用于我的用例。

我尝试将方法appendNext()转换为Kotlin中的扩展函数。但是显然Kotlin似乎没有找到方法Logger.message()并抛出NoSuchMethodError

在下面的代码段中,我给出了到目前为止尝试过的原始Java代码和Kotlin代码

我希望在不使用@JvmDefault批注的情况下使用此代码的Kotlin惯用解决方案。如果没有的话,代码应该和Java一样简洁。任何帮助将不胜感激。

Java代码

这是设计模式“责任链”的正确运行的Java代码

import java.util.Arrays;
import java.util.EnumSet;
import java.util.function.Consumer;

@FunctionalInterface
public interface Logger {
    public enum LogLevel {
        INFO,DEBUG,WARNING,ERROR,FUNCTIONAL_MESSAGE,FUNCTIONAL_ERROR;

        public static LogLevel[] all() {
            return values();
        }
    }

    abstract void message(String msg,LogLevel severity);

    default Logger appendNext(Logger nextLogger) {
        return (msg,severity) -> {
            message(msg,severity);
            nextLogger.message(msg,severity);
        };
    }

    static Logger writeLogger(LogLevel[] levels,Consumer<String> stringConsumer) {
        EnumSet<LogLevel> set = EnumSet.copyOf(Arrays.asList(levels));
        return (msg,severity) -> {
            if (set.contains(severity)) {
                stringConsumer.accept(msg);
            }
        };
    }

    static Logger consoleLogger(LogLevel... levels) {
        return writeLogger(levels,msg -> System.err.println("Writing to console: " + msg));
    }

    static Logger emailLogger(LogLevel... levels) {
        return writeLogger(levels,msg -> System.err.println("Sending via email: " + msg));
    }

    static Logger fileLogger(LogLevel... levels) {
        return writeLogger(levels,msg -> System.err.println("Writing to Log File: " + msg));
    }

    public static void main(String[] args) {
        // Build an immutable chain of responsibility
        Logger logger = consoleLogger(LogLevel.all())
                .appendNext(emailLogger(LogLevel.FUNCTIONAL_MESSAGE,LogLevel.FUNCTIONAL_ERROR))
                .appendNext(fileLogger(LogLevel.WARNING,LogLevel.ERROR));

        // Handled by consoleLogger since the console has a LogLevel of all
        logger.message("Entering function ProcessOrder().",LogLevel.DEBUG);
        logger.message("Order record retrieved.",LogLevel.INFO);

        // Handled by consoleLogger and emailLogger since emailLogger implements Functional_Error & Functional_Message
        logger.message("Unable to Process Order ORD1 Dated D1 For Customer C1.",LogLevel.FUNCTIONAL_ERROR);
        logger.message("Order dispatched.",LogLevel.FUNCTIONAL_MESSAGE);

        // Handled by consoleLogger and fileLogger since fileLogger implements Warning & Error
        logger.message("Customer Address details missing in Branch DataBase.",LogLevel.WARNING);
        logger.message("Customer Address details missing in Organization DataBase.",LogLevel.ERROR);
    }
}

科特琳码

这是我到目前为止尝试过的。我将Enum移到了一个单独的文件,并将所有内容都保留在顶层。看一下appendNext()方法,这似乎是问题的原因。

Logger.kt

import java.util.*
import java.util.function.Consumer

interface Logger {
    fun message(message: String,severity: LogLevel)
}

fun Logger.appendNext(nextLogger: Logger): Logger {
    return object: Logger {
        override fun message(message: String,severity: LogLevel) {
            message(message,severity)
            nextLogger.message(message,severity)
        }
    }
}

fun writeLogger(
    stringConsumer: Consumer<String>,vararg levels: LogLevel
): Logger {
    val set = EnumSet.copyOf(listof(*levels))
    return object: Logger {
        override fun message(message: String,severity: LogLevel) {
            if (set.contains(severity)) {
                stringConsumer.accept(message)
            }
        }
    }
}

fun consoleLogger(vararg levels: LogLevel): Logger {
    return writeLogger(
        Consumer { msg: String -> System.err.println("Writing to console: $msg") },*levels
    )
}

fun emailLogger(vararg levels: LogLevel): Logger {
    return writeLogger(
        Consumer { msg: String -> System.err.println("Sending via email: $msg") },*levels
    )
}

fun fileLogger(vararg levels: LogLevel): Logger {
    return writeLogger(
        Consumer { msg: String -> System.err.println("Writing to Log File: $msg") },*levels
    )
}

fun main() {
    // Build an immutable chain of responsibility
    val logger = consoleLogger(*LogLevel.all())
        .appendNext(emailLogger(LogLevel.FUNCTIONAL_MESSAGE,LogLevel.FUNCTIONAL_ERROR))
        .appendNext(fileLogger(LogLevel.WARNING,LogLevel.ERROR))

    // Handled by consoleLogger since the console has a LogLevel of all
    logger.message("Entering function ProcessOrder().",LogLevel.DEBUG)
    logger.message("Order record retrieved.",LogLevel.INFO)

    // Handled by consoleLogger and emailLogger since emailLogger implements Functional_Error & Functional_Message
    logger.message("Unable to Process Order ORD1 Dated D1 For Customer C1.",LogLevel.FUNCTIONAL_ERROR)
    logger.message("Order dispatched.",LogLevel.FUNCTIONAL_MESSAGE)

    // Handled by consoleLogger and fileLogger since fileLogger implements Warning & Error
    logger.message("Customer Address details missing in Branch DataBase.",LogLevel.WARNING)
    logger.message("Customer Address details missing in Organization DataBase.",LogLevel.ERROR)
}

LogLevel.kt

enum class LogLevel {
    INFO,FUNCTIONAL_ERROR;

    companion object {
        public fun all(): Array<LogLevel> {
            return values()
        }
    }
}

解决方法

我不明白为什么您添加了原始Java代码中不存在的currentLogger属性。

如果您希望获得与Java相同的行为,其中一个实现可以覆盖appendNext()的默认实现,则它看起来像这样:

fun interface Logger {
    fun message(message: String,severity: LogLevel)

    fun appendNext(nextLogger: Logger): Logger {
        return Logger { message,severity ->
            message(message,severity)
            nextLogger.message(message,severity)
        }
    }
}

如果您不打算覆盖此功能,则将其移至扩展功能会更合适。然后“覆盖”它将需要组成另一个具有相同签名的扩展功能,然后导入该扩展功能以使用它。这是标准库功能的组织方式。仍然不是万无一失,但是将功能放在界面中会更强烈地表明它被 meant 覆盖。

fun interface Logger {
    fun message(message: String,severity: LogLevel)
}

fun Logger.appendNext(nextLogger: Logger): Logger {
    return Logger { message,severity ->
        message(message,severity)
        nextLogger.message(message,severity)
    }
}

编辑:另外,由于Kotlin中的函数是一流的类型,因此您不必使用Consumer。例如,将Consumer<String>替换为(String) -> Unit,然后直接使用stringConsumer(message)而不是stringConsumer.accept(message)调用它。

,

此处最佳解决方案的关键是使用Functional interfaces(在Kotlin 1.4中引入)

另外,为使代码更简洁明了,请考虑使用single-expression fuctions,删除可能推断出的类型,并在不创建额外对象的情况下将EnumSet.copyOf(listOf(*levels))重写为辅助函数:

import java.util.*
import java.util.function.Consumer

fun interface Logger {
    fun message(msg: String,severity: LogLevel)
}

fun Logger.appendNext(nextLogger: Logger) = Logger { msg,severity ->
    message(msg,severity)
    nextLogger.message(msg,severity)
}

inline fun <reified E : Enum<E>> enumSetOf(e: Array<out E>): EnumSet<E> =
    EnumSet.noneOf(E::class.java).also { result -> e.forEach { result.add(it) } }

fun writeLogger(levels: Array<out LogLevel>,stringConsumer: Consumer<String>) = Logger { msg,severity ->
    if (severity in enumSetOf(levels)) {
        stringConsumer.accept(msg)
    }
}

fun consoleLogger(vararg levels: LogLevel) =
    writeLogger(levels) { msg -> System.err.println("Writing to console: $msg") }

fun emailLogger(vararg levels: LogLevel) =
    writeLogger(levels) { msg -> System.err.println("Sending via email: $msg") }

fun fileLogger(vararg levels: LogLevel) =
    writeLogger(levels) { msg -> System.err.println("Writing to Log File: $msg") }

fun main() {
    // Build an immutable chain of responsibility
    val logger = consoleLogger(*LogLevel.all())
        .appendNext(emailLogger(LogLevel.FUNCTIONAL_MESSAGE,LogLevel.FUNCTIONAL_ERROR))
        .appendNext(fileLogger(LogLevel.WARNING,LogLevel.ERROR))

    // Handled by consoleLogger since the console has a LogLevel of all
    logger.message("Entering function ProcessOrder().",LogLevel.DEBUG)
    logger.message("Order record retrieved.",LogLevel.INFO)

    // Handled by consoleLogger and emailLogger since emailLogger implements Functional_Error & Functional_Message
    logger.message("Unable to Process Order ORD1 Dated D1 For Customer C1.",LogLevel.FUNCTIONAL_ERROR)
    logger.message("Order Dispatched.",LogLevel.FUNCTIONAL_MESSAGE)

    // Handled by consoleLogger and fileLogger since fileLogger implements Warning & Error
    logger.message("Customer Address details missing in Branch DataBase.",LogLevel.WARNING)
    logger.message("Customer Address details missing in Organization DataBase.",LogLevel.ERROR)
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?