在 Java 中表达匿名交集类型 [英] Expressing anonymous intersection types in Java

查看:34
本文介绍了在 Java 中表达匿名交集类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这些都编译不过......我想表达匿名 Java 中的交叉类型 使得 (A & B) 表示实现/扩展 A 和 B 的任何类型接口,但通过将类型组合成一些语法(如 &)以匿名方式在代码中表达.我特别想在以下情况下使用它们:

  1. 方法参数public void foo((A & B) x) { ... }
  2. 方法返回类型public (A & B) foo() { ... }
  3. 局部变量(A & B) foo = ...
  4. 通配符泛型<代码>列表foo = ...

我认为我现在的选项(Java 13)如下是否正确?

  1. 方法参数 - 使用泛型:public static <T extends A &乙>void foo(T x) { ... }
  2. 方法返回类型 - 使用泛型:public static <T extends A &乙>T foo() { ... }
  3. 局部变量 - 使用 var var foo = (A & B) bar
  4. 通配符泛型 - 当前没有选项

以下文章深入探讨了这些类型何时/为何有用.这篇文章很好地涵盖了 1. 这个 文章 涵盖了 1. 2. 和 3. 这个 SO question 涵盖 4. Java 没有语法是多么奇怪.

--- 按要求编辑以解释我的用例---

我经常使用仅包含信息项的 Value 对象.这些信息项相对可独立重用,但通常只是为了方便而将它们组合在一起,并传递给其他应用实际逻辑的类.我发现以一种稳定且可长期重用的方式命名 getter 接口非常容易.关于 getCustomerForename()getCustomerId() 将返回什么,几乎没有争论,定义这些接近于为您的业务定义数据模型".但是,一旦您尝试将它们连接在一起以形成 Customer 界面,该界面通常会变得非常特殊,并且会被不当重用,因为在某些情况下您需要所有客户信息,而在其他情况下您只需要一个很少,或者混合了其他数据.很难命名,创建耦合(无论是否需要)并创建额外的代码.相反,我想避免创建/命名任何新接口,而是使用 匿名交叉类型 从现有接口中动态创建新类型,一次只公开一个 getter.所以上面的接口AB 就是下面这样的接口:

interface CustomerId {长 getCustomerId();}接口客户名称{String getCustomerForename();}

...如果我有一份报告显示了这两个项目,我只会创建具有类型"(CustomerId & CustomerForename) 的对象,而不是命名和创建 CustomerIdAndCustomerForename 接口/类几乎没有什么好处.

解决方案

如果你有一个类型参数指定为 你可以在通配符类型中使用它在以下示例中使用 Optional.这是相似的,但我想和你想做的不一样,虽然现在我的大脑太痛了,无法解释有什么区别.

<预><代码>公共类泛型测试{公共静态无效主(字符串 [] args){System.out.println(generateCombination().get().getB());System.out.println(generateCombination().get().getA());}static <T extends A&B>可选生成组合(){return (Optional) Optional.of(new Both());}接口A{字符串 getA();}接口B{数字 getB();}静态类都实现了 A, B {@覆盖公共字符串 getA() {return "你好";}@覆盖公众号 getB() {返回 23;}}}

无论如何,如您的问题所述,其他变体似乎是正确的.

None of these compile but ... I would like to express anonymous intersection types in Java such that (A & B) means any type that implements/extends both A and B interfaces, but expressed anonymously in code by composing the types with some syntax like &. In particular I'd like to use them in the following circumstances:

  1. method parameters e.g. public void foo((A & B) x) { ... }
  2. method return types e.g. public (A & B) foo() { ... }
  3. local variables e.g. (A & B) foo = ...
  4. wildcard generics e.g. List<? extends A & B)> foo = ...

Am I right in thinking that the options for me as of now (Java 13) are as follows?

  1. method parameters - use generics: public static <T extends A & B> void foo(T x) { ... }
  2. method return types - use generics: public static <T extends A & B> T foo() { ... }
  3. local variables - use var var foo = (A & B) bar
  4. wildcard generics - no current option

The following articles dig deeper into when/why these types can be useful. This article nicely covers 1. This article covers 1. 2. and 3. This SO question covers 4. and how curious it is that Java has no syntax for this.

--- edited to explain my use case as requested ---

I often use Value objects holding only items of information. These information items are relatively re-usable independently but are often just globbed together for convenience and passed around other classes that apply actual logic with them. I find it's very easy to name getter interfaces in a way that's stable and reusable long term. There's very little debate about what getCustomerForename() and getCustomerId() will return, and defining these is close to defining a "data model" for your business. But once you try and bolt them together to make say a Customer interface, that interface often becomes very special purpose and innappropriately reused as in some contexts you need all the customer info, where in others you only need a few, or with other pieces of data mixed in. It's hard to name, creates coupling whether needn't be any and creates extra code. Instead I want to avoid creating/naming any new interfaces and instead use an anonymous intersection type creating a new type on the fly from existing ones that expose just one getter at a time. So the interfaces A and B in the above would be interfaces such as the following:

interface CustomerId {
  Long getCustomerId();
}

interface CustomerForename {
  String getCustomerForename();
}

... and if I had a report that just surfaced those 2 items, I'd just create objects with the "type" (CustomerId & CustomerForename) instead of naming and creating a CustomerIdAndCustomerForename interface/class for very little benefit.

解决方案

If you have a type parameter specified as <T extends A&B> you can use it in wildcard types as is done in the following example using Optional<? extends T>. Which is similar, but I guess not the same as what you want to do, although right now my brain hurts too much to explain what the difference is.


public class GenericsTest {

    public static void main(String[] args) {

        System.out.println(generateCombination().get().getB());
        System.out.println(generateCombination().get().getA());
    }

    static <T extends A&B> Optional<? extends T> generateCombination() {
        return (Optional<? extends T>) Optional.of(new Both());
    }

     interface A {
        String getA();
    }

     interface B {
        Number getB();
    }

    static class Both implements A, B {
        @Override
        public String getA() {
            return "Hello";
        }

        @Override
        public Number getB() {
            return 23;
        }
    }
}

Anyway the other variants seem to be correct as described in your question.

这篇关于在 Java 中表达匿名交集类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆