【问题标题】:Add bound to class type parameter in method在方法中添加绑定到类类型参数
【发布时间】:2017-10-25 10:53:54
【问题描述】:

有什么方法可以在具体方法中使类类型参数更窄(添加另一个绑定)?

我们来看例子

public class Value<T>
{
    private final T value;

    public Value(T value)
    {
        this.value = value;
    }

    public <V extends T> boolean eq(V value)
    {
        return Objects.equals(this.value, value);
    }

    // here, I want to create bound that T extends Comparable<T>
    // error: type parameter cannot be followed by other bounds
    public <V extends T & Comparable<T>> boolean gt(V value)
    {
        return ((V)this.value).compareTo(value) > 0;
    }

    // here, I want to create bound that T extends String
    // error: interface expected here
    public <V extends T & String> boolean match(V value)
    {
        return ((V)this.value).equalsIgnoreCase(value);
    }

    public static void main(final String[] args)
    {
        final Value<Integer> integerValue = new Value<>(10);
        integerValue.eq(10);           // should compile
        integerValue.gt(5);            // should compile
        integerValue.match("hello");   // shouldn't compile because match operates only on String values

        final Value<String> stringValue = new Value<>("Foo");
        stringValue.eq("Foo");         // should compile
        stringValue.gt("bar");         // should compile
        stringValue.match("foo");      // should compile
    }
}

在此示例行中

integerValue.match("hello");

不编译,这是正确的,但是由于类型参数不能跟随其他边界

的限制,该类也无法编译

还有其他方法可以实现吗?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    一个实例方法必须对类的所有实例都可用。您不能声明仅存在于类的某些实例(具有某些类型参数的实例)的方法。

    您可以做的是创建一个通用静态方法,该方法将类的实例作为参数。由于泛型类型参数现在特定于方法,因此可以限制为方法想要的:

    public static <T> boolean eq(Value<T> obj, T value)
    {
        return Objects.equals(obj.value, value);
    }
    
    public static <T extends Comparable<T>> boolean gt(Value<T> obj, T value)
    {
        return obj.value.compareTo(value) > 0;
    }
    
    public static <T extends String> boolean match(Value<T> obj, T value)
    {
        return obj.value.equalsIgnoreCase(value);
    }
    

    【讨论】:

      【解决方案2】:

      使用&amp; 运算符假定V 直接扩展TString,因此其中一个必须是接口,因为一个类不能直接扩展另外两个类。它也不适用于 Comparable&lt;T&gt;,因为编译器无法保证该错字的类型安全。

      你只需要使用,

      // here, I want to create bound that T extends Comparable<T>
      // error: type parameter cannot be followed by other bounds
      public <V extends T, T extends Comparable<T>> boolean gt(V value)
      {
          return ((V)this.value).compareTo(value) > 0;
      }
      
      // here, I want to create bound that T extends String
      // error: interface expected here
      public <V extends T, T extends String> boolean match(V value)
      {
          return ((V)this.value).equalsIgnoreCase(value);
      }
      

      【讨论】:

      • 我知道,但这将引入新的 T 类型参数,它隐藏了类类型参数 T,并且带有 integerValue.match("hello") 的行现在将编译。
      • 类声明中的限制怎么样:class Value> ?
      • 我不能在类声明中限制类型,因为你可以创建你想要的任何类型的值,但是只有当你的类型扩展 Comparable 时 gt 操作才应该编译。
      • 如果 T 在返回布尔值之前没有扩展 Comparable 会抛出异常吗?
      • 是的,这就是我现在正在做的事情。我只是想确保没有其他可能的类型安全的解决方案。
      【解决方案3】:

      类型界限不是您需要的解决方案。你需要的是子类。

      public class Value<T>
      {
          protected final T value;
      
          public Value(T value)
          {
              this.value = value;
          }
      
          public boolean eq(T value)
          {
              return Objects.equals(this.value, value);
          }
      }
      
      public class ComparableValue<T extends Comparable<T>> extends Value<T>
      {
          public ComparableValue(T value)
          {
              super(value);
          }
      
          public boolean gt(T value)
          {
              return this.value.compareTo(value) > 0;
          }
      }
      
      public class StringValue extends ComparableValue<String>
      {
          public StringValue(String value)
          {
              super(value);
          }
      
          public boolean match(String value)
          {
              return this.value.equalsIgnoreCase(value);
          }
      }
      

      那么main就变成了

          public static void main(final String[] args)
          {
              final ComparableValue<Integer> integerValue = new ComparableValue<>(10);
              integerValue.eq(10);           // will compile
              integerValue.gt(5);            // will compile
              integerValue.match("hello");   // will not compile
      
              final StringValue stringValue = new StringValue("Foo");
              stringValue.eq("Foo");         // will compile
              stringValue.gt("bar");         // will compile
              stringValue.match("foo");      // will compile
          }
      

      【讨论】:

      • 我也是这么想的。一个缺点是可能存在多个子类中可用的操作,并且您不能从另一个继承一个,因此会出现代码重复。另一个缺点是,客户端必须选择正确的子类来使用。在我的情况下,大约有 10 个子类,所以会很烦人。
      • 是的,但我认为没有其他方法可以进行编译时检查。
      猜你喜欢
      • 2018-05-27
      • 2016-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多