如何解决如何习惯性地在Kotlin中编写Java的功能接口的默认方法?
我正在尝试惯用地转换Kotlin中称为Chain of Responsibility的设计模式的Java代码。但是我对在Kotlin中转换Java接口的默认方法appendNext()
毫无头绪。我尝试了一些已经存在的问题,例如this和this,但它们似乎不适用于我的用例。
我尝试将默认方法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 举报,一经查实,本站将立刻删除。