跟踪构建器在 Java 类型系统中添加的接口

Tracking interfaces added by a builder in the Java type system

提问人:sagev 提问时间:11/17/2023 最后编辑:sagev 更新时间:11/17/2023 访问量:64

问:

如果我有一个构建器类,有没有办法跟踪在类型级别添加了某些接口的事实?

例如,如果我有

var dinner = Noodling.build()
  .name("Spaghetti") // Returns PastaBase & IName
  .sauce("Marinara") // Returns PastaBase & ISauce, but I also want `& IName`

我想让这个编译

String name = dinner.getName();

但事实并非如此,因为没有保持这种类型。dinnerIName

这是目前我最接近这种模式的工作方式,但它仍然有这个问题。

public class Noodling {

  public static PastaBase build() { return new Noodle(); }

  public interface IName { String getName(); }
  public interface ISauce { String getSauce(); }

  public interface PastaBase {
    public <T extends PastaBase & IName> T name();
    public <T extends PastaBase & ISauce> T sauce();
  }

  public static class PastaImpl implements PastaBase, IName, ISauce {

    @Override public <T extends PastaBase & IName> T name() { return (T) this; }
    @Override public <T extends PastaBase & ISauce> T sauce() { return (T) this; }

    @Override public String getName() { return name; }
    @Override public String getSauce() { return sauce; }

  }
}

如果我想要的超出了 Java 类型系统的可能性,我不会感到震惊,但它似乎非常接近!

Java 泛型 接口

评论

0赞 sagev 11/17/2023
没有重命名我想要的一切。ISummary 应该是 IName。编辑。
0赞 Basil Bourque 11/17/2023
您尚未定义 .Pasta
0赞 sagev 11/17/2023
编辑。也许根本问题现在已经很清楚了:)

答:

0赞 Jorn 11/17/2023 #1

不,这是不可能的。Java 没有动态类型。所有类型都必须在编译时已知,并且没有办法传递“你已经调用了方法,所以它现在也是一个”。nameIName

不过,也有解决方法。如果你的对象是可变的(、、等都返回相同的实例),你可以将调用结果分配给一个类型的变量,并调用它。或者,您可以为每个接口组合创建类,并根据调用的方法返回这些类。但这是很多代码,很可能没有太大的好处。namesaucenameINamegetName()

评论

0赞 sagev 11/17/2023
这就是我降落的地方。感觉类型系统只是一根头发短,因为它在编译时确实拥有所有信息(例如,它知道返回一个),但是如果没有构建一个可怕的接口树,就没有办法在这些调用之间携带这些信息。name()IName