Java泛型-需要解释这个Log4j 2类的签名- RollingFileAppender.Builder

7gcisfzg  于 2023-05-12  发布在  Java
关注(0)|答案(2)|浏览(228)

有没有人能给我解释一下这个班级的签名是什么意思-
org.apache.logging.log4j.core.appender
Class RollingFileAppender.Builder<B extends RollingFileAppender.Builder<B>>
如文件所示-Log4j2 apidocs
我把它读作Builder,表示类型B,其中Bextends表示类型B* 的 * 生成器,这让我很困惑。
另外,这是一个static类,它也有一个构造函数。这正常吗?有人能分享一个参考资料,可以帮助我理解和使用这个程序?
PS -关于我在这里做什么-我正在修改log4j代码,使其与log4j 2兼容,其中有一个RollingFileAppender使用util类中的RollingFileAppender构造函数创建。

pbgvytdp

pbgvytdp1#

构建器通常有一个流畅的API。也就是说,它们的构建方法返回builder类,以便能够直接调用其他构建方法。例如,有问题的类RollingFileAppender.Builder可以这样使用:

var appender = builder.withAppend(true).withLocking(true).build();

这意味着构建方法(在本例中以with为前缀的方法)必须返回相同的构建器示例。但是...如果你设计了一个被子类化的构建器类,你就有问题了。构建方法应返回什么类型?
考虑以下类:

abstract class Builder {
    abstract Object build();
    Builder withProperty(String property) { return this; }
}
class ChildBuilder extends Builder {
    Object build() { return new Object(); }
    ChildBuilder withChildProperty(String childProperty) { return this; }
}

build方法的实际返回类型与本说明无关。现在的问题是。。您不能执行以下操作:

var obj = new ChildBuilder()
    .withProperty("property")
    .withChildProperty("child property") // Compiler error here!
    .build();

withProperty的返回类型是Builder,并且该类型不知道构建方法withChildProperty。所以你破坏了流畅的API。
解决方案:参数化生成器类。下面示例中的类型参数B旨在表达 *self类型 * 的概念。
对于我们的示例,您执行以下操作:

abstract class Builder<B extends Builder<B>> {
    abstract Object build();
    B withProperty(String property) { return this; } // Note the return type here.
}
final class ChildBuilder extends Builder<ChildBuilder> {
    Object build() { return new Object(); }
    ChildBuilder withChildProperty(String childProperty) { return this; }
}

现在你可以像我上面提到的那样做代码了。

8i9zcol2

8i9zcol22#

这被称为curiously recurring template pattern
据我所知,RollingFileAppender.Builder没有任何子类,所以从技术上讲,RollingFileAppender.Builder不需要这样做。它可以被声明为非泛型类型:

public static class RollingFileAppender.Builder extends 
    AbstractOutputStreamAppender.Builder<RollingFileAppender.Builder>

及其生成器方法,它们都返回B(例如,withAdvertise),也可以返回RollingFileAppender.Builder
然而,考虑这样一种情况,有人试图编写这个非泛型类的子类:

public class FooBuilder extends RollingFileAppender.Builder {
    public FooBuilder withFoo(String foo) {
        // ...
        return this;
    }
}

// Let's try using MyBuilder:
new FooBuilder()
    .withAdvertise(false) // let's call one of the methods in RollingFileAppender.Builder first...
    .withFoo("Foo") // and now we try to call my subclass's method
    .build()

调用withFoo无法编译。这是因为在我们假设的非泛型RollingFileAppender.Builder中,withAdvertise返回RollingFileAppender.Builder,而不是MyBuilder
RollingFileAppender.Builder设为泛型,并使其生成器方法返回泛型类型参数B,就可以解决这个问题。因为我们可以这样写:

// in this pattern,
// you are always expected to put the class that you are declaring as B
public class MyBuilder extends RollingFileAppender.Builder<MyBuilder>

RollingFileAppender.Builder中声明的任何返回B的方法现在都将返回MyBuilder

new MyBuilder()
    .withAdvertise(false) // this now returns a MyBuilder
    .withFoo("Foo") // and this now works
    .build()

查看RollingFileAppender.Builder的继承层次结构也会很有帮助:

java.lang.Object
    org.apache.logging.log4j.core.filter.AbstractFilterable.Builder<B>
        org.apache.logging.log4j.core.appender.AbstractAppender.Builder<B>
            org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.Builder<B>
                org.apache.logging.log4j.core.appender.RollingFileAppender.Builder<B>

如您所见,这个类继承了许多构建器方法。如果没有一个类遵循这种奇怪的重复模板模式,那么在使用了从RollingFileAppender.Builder的超类继承的方法之后,您将无法调用在RollingFileAppender.Builder中声明的方法。

new RollingFileAppender.Builder()
    .setImmediateFlush(true) // this is declared in AbstractOutputStreamAppender.Builder
    // so the above would have returned an AbstractOutputStreamAppender.Builder
    .withAdvertise(true); // now you wouldn't be able to do this

总而言之,这个B只是所有构建器方法返回您正在使用的Builder子类的一种方式,而不是声明它们的类。虽然RollingFileAppender.Builder不需要它,因为它没有子类,但作者仍然这样声明它,大概是为了遵循模式。
至于为什么static类可以有构造函数,static类只是意味着你不需要外部类(RollingFileAppender)的示例来示例化嵌套类。它不阻止声明构造函数。

相关问题