如何在Java中保留类型变量的原始类型?

时间:2013-10-08 15:12:27

标签: java

我有以下示例:

class bounds
{
  private StringBuilder str = new StringBuilder();

  public <Type> void add (Type value)
  {
    add_specific (value);
    str.append (String.format("%n"));
  }

  private <Type extends Number> void add_specific (Type value)
  {
    str.append (value);
  }

  public String toString () { return str.toString(); }

  public static void main (String[] args)
  {
    bounds test = new bounds();
    test.add (new Integer (42));
    System.out.print (test.toString());
  }
}

当我尝试编译它时,我收到以下错误:

bounds.java:7: error: method add_specific in class bounds cannot be applied to given types;
    add_specific (value);
    ^
  required: Type#1
  found: Type#2
  reason: inferred type does not conform to declared bound(s)
    inferred: Type#2
    bound(s): Number
  where Type#1,Type#2 are type-variables:
    Type#1 extends Number declared in method add_specific(Type#1)
    Type#2 extends Object declared in method add(Type#2)
1 error

这看起来好像传递给add方法的参数的原始类型在add的主体中丢失了。如何保留类型以便选择正确的add_specific方法?

更新

我简化了我的例子,因为我觉得它更容易理解。但在我看来,大多数人都不理解它包含泛型和特定功能的原因。所以我粘贴了一个更高级的例子。也许这会使原因更加明显:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

class bounds
{
  private StringBuilder str = new StringBuilder();

  public <Type> void add (Type value)
  {
    add_specific (value);
    str.append (String.format("%n"));
  }

  private <Type extends Number> void add_specific (Type value)
  {
    str.append (value);
  }

  private void add_specific (String value)
  {
    str.append ('"');
    for (int i = 0; i < value.length(); i++) {
      char ch = value.charAt(i);
      switch (ch) {
      case '\\': str.append ("\\\\"); break;
      case '"': str.append ("\\\""); break;
      default: str.append (ch);
      }
    }
    str.append ('"');
  }

  private static DateFormat iso8601
    = new SimpleDateFormat("'\"'yyyy-MM-dd'T'HH:mm:ssZ'\"'");

  private void add_specific (Date date)
  {
    str.append (iso8601.format(date));
  }

  public String toString ()
  {
    return str.toString();
  }

  public static void main (String[] args)
  {
    bounds test = new bounds();

    test.add (new Integer (42));
    test.add ("42");
    test.add (new Date());

    System.out.print (test.toString());
  }
}

我有一个名为add的通用函数。这个泛型函数做了一些通用的函数,并调用特定函数来执行特定的操作。问题是,选择特定函数的类型在泛型函数中丢失了。问题是如何解决这个问题?我如何编写泛型函数,以便仍然可以在泛型函数体中选择正确的特定函数?

4 个答案:

答案 0 :(得分:3)

  

这看起来好像传递给add方法的参数的原始类型在add的主体中丢失了。

嗯,类型擦除意味着在执行时不会知道Type,但这并不是你得到编译时错误的直接原因。您可以使用任何类型调用add - 而您只能使用与add_specific兼容的类型调用Number。例如,考虑一下:

// TODO: Fix your naming to meet Java naming conventions
bounds b = new bounds();
b.<String>add("foo");

您希望如何致电add_specificString不会延伸Number

(顺便说一句,命名类型参数Type也是一个真的坏主意 - 它很容易与java.lang.reflect.Type混淆。)

选项:

  • extends Number
  • 中添加Type绑定到add的{​​{1}}
  • Type
  • 中移除add_specific的绑定

编辑:好的,所以听起来你只给了我们一半的图片 - 你期望在执行时基于Type执行重载决策。这不会起作用,原因有两个:

  • Java 始终在编译时执行重载解析
  • 类型擦除意味着在执行时不存在信息,无论如何都要基于过载决策。如果您需要该信息,则需要另一个Class<Type>类型的参数。

答案 1 :(得分:1)

问题在于这一部分:

public <Type> void add (Type value)
{
   add_specific (value);
   ...
}

add_specific要求值为Number扩展的类型,但add不需要/确保该值。因此错误。

要解决此问题,您需要更改add以使用<Type extends Number>,但我怀疑这是您想要的。这对我来说似乎是一个设计错误:add_specific应该调用更通用的add,而不是相反。{/ p> 正如John Skeet所说,请遵守编码惯例,这样可以更容易发现错误并阅读您的代码。

答案 2 :(得分:0)

编译器正在尖叫,因为value方法中的引用add不一定是Number

解决这个问题:

add方法更改为:

public <Type extends Number> void add (Type value)
{

}

或添加:

public <Type> void add (Type value)
  {
    if (value instanceof Number){
        add_specific ((Number)value);
        str.append (String.format("%n"));
    }
  }

答案 3 :(得分:0)

删除add和add_specific方法,只是重载add方法。 但要注意,选择哪种方法是在编译时静态 完成 ,而不是在运行时动态完成。

像乔恩说的那样,你想做的事情不会奏效。如果您希望所有客户端代码看起来都一样,那么执行以下操作;虽然在Effective Java中,Bloch建议你避免使用这种令人困惑的API。

class Bounds {   
   private StringBuilder str = new StringBuilder();

  private void appendNewline() {
       str.append (String.format("%n"));
  }
  public  void add (Number value)   {
    str.append (value);   
    appendNewline();
  }

  public void add (String value)   {
    str.append ('"');
    for (int i = 0; i < value.length(); i++) {
      char ch = value.charAt(i);
      switch (ch) {
      case '\\': str.append ("\\\\"); break;
      case '"': str.append ("\\\""); break;
      default: str.append (ch);
      }
    }
    str.append ('"');  
  appendNewline();
 }

  private static DateFormat iso8601
    = new SimpleDateFormat("'\"'yyyy-MM-dd'T'HH:mm:ssZ'\"'");

  public void add (Date date)   {
    str.append (iso8601.format(date));   
    appendNewline();
  }

  public String toString ()   {
    return str.toString();   }

  public static void main (String[] args)   {
    bounds test = new bounds();

    test.add (new Integer (42));
    test.add ("42");
    test.add (new Date());

    System.out.print (test.toString());   } 
}