Java泛型:实际参数T不能通过方法调用转换转换为int

时间:2015-01-23 11:45:11

标签: java generics

我有这样的代码:

// This class cannot be changed
class VendorApi {
        static void func1(char x) {}
        static void func1(int x) {}
        static void func1(float x) {}
        static void func1(double x) {}
}

class Main {
          static <T> void my_func(T arg) {
                  // much of code, which uses T
                  // ...
                  VendorApi.<T>func1(arg);
          }

          public static void main(String args[]) {
                  // call my_func for each type (char, int, float, double)
                  // ...
                  int i = 1;
                  my_func(i);
                  char c = 1;
                  my_func(c);
          }
}

我需要做的是从my_func()为每种类型的参数调用每个函数VendorApi.func()。发布的代码没有编译,它显示了一个想法。除了为每种类型复制粘贴my_func()之外,我该怎么做?

7 个答案:

答案 0 :(得分:6)

您可以将func1作为Consumer<T>传递给方法:

class VendorApi {
    static void func1(char x) {}
    static void func1(int x) {}
    static void func1(float x) {}
    static void func1(double x) {}
}

class Main {
      static void my_func(char arg) {  my_func(arg, VendorApi::func1); }
      static void my_func(int arg) {  my_func(arg, VendorApi::func1); }
      static void my_func(float arg) {  my_func(arg, VendorApi::func1); }
      static void my_func(double arg) {  my_func(arg, VendorApi::func1); }
      private static <T> void my_func(T arg, Consumer<T> func1) {
          // much of code, which uses T
          // ...
          func1.accept(arg);
      }

      public static void main(String args[]) {
          // call my_func for each type (char, int, float, double)
          // ...
          int i = 1;
          my_func(i, VendorApi::func1);
          char c = 1;
          my_func(c);
      }
}

这为您提供了编译时类型安全性(您只能从课程外部使用my_funccharintfloat来呼叫double通用版本是私有的)并避免反射。

如果您想遵循Java方法命名约定,my_func也应该是myFunc

答案 1 :(得分:4)

不是最干净的答案,但它会按照你的要求行事。

您可以测试您的通用参数类是否与VenderApi提供的任何类型匹配,然后进行强制转换。

<强>代码

public class Main {
    static <T> void my_func(T arg) {
        if (arg.getClass().equals(Integer.class))
            VendorApi.func1((Integer) arg);
        else if (arg.getClass().equals(Character.class))
            VendorApi.func1((Character) arg);
        else
            throw new IllegalStateException(
                    "cannot perform my_func on object of class "
                            + arg.getClass());
    }

    public static void main(String args[]) {
        // call my_func for each type (char, int, float, double)
        // ...
        int i = 1;
        my_func(i);
        char c = 1;
        my_func(c);
        String str = "bla";
        my_func(str);
    }
}

您的供应商API

//This class cannot be changed
public class VendorApi {
    public static void func1(char x) {
        System.out.println("i am a char "+x);
    }

    public static void func1(int x) {
        System.out.println("i am a int "+x);
    }

    public static void func1(float x) {
    }

    public static void func1(double x) {
    }
}

<强>输出

i am a int 1
i am a char 
Exception in thread "main" java.lang.IllegalStateException: cannot perform my_func on object of class class java.lang.String
    at core.Main.my_func(Main.java:10)
    at core.Main.main(Main.java:23)

答案 2 :(得分:4)

另一种选择是使用反射和映射:

Map<Class<?>, Method> mapping = new HashMap<>();
mapping.put(Integer.class, VendorApi.class.getMethod("func1", int.class));
// more mappings here

尽管您需要与if / else构造一样多的代码,但也可以通过编程方式填充此映射(您可以在VendorApi.class.getMethods()周围运行循环),也可以从文件中读取配置。总而言之,这种映射更灵活。

现在您可以使用它来调用API:

static void callVendorFunc(Object arg) {   // no need for generics here
    mapping.get(arg.getClass()).invoke(null, arg);
}

你的方法变成了:

static <T> void my_func(T arg) {
    // much of code, which uses T
    // ...
    callVendorFunc(arg);
}

我没有照顾任何例外。而且 - 当然 - 反思方法的表现稍差。

答案 3 :(得分:3)

您正在寻找的内容无法在java中完成。这是一种解决方法。

// This class cannot be changed
class VendorApi {
    static void func1(char x) {}
    static void func1(int x) {}
    static void func1(float x) {}
    static void func1(double x) {}
}

class Main {
    static <T> void my_func(T arg) {
        // much of code, which uses T
        // ...
        if(arg instanceof Character) {
            VendorApi.func1((Character)arg);
        }
        else if (arg instanceof Integer) {
            VendorApi.func1((Integer)arg);
        }
        //And so on...
    }

    public static void main(String args[]) {
        // call my_func for each type (char, int, float, double)
        // ...
        int i = 1;
        my_func(i);
        char c = 1;
        my_func(c);
    }
}

但我会建议重新考虑你的设计。

答案 4 :(得分:3)

如果您无法更改VendorApi,那么您的最佳选择似乎就是将其包含在通用调用中。这与其他人建议的类似,但复制较少,并且类型安全(如果参数类型错误,则没有运行时异常):

class VendorAPIWrapper {
    static <T extends Number> void func1(T arg) {
        if(arg instanceof Double) VendorAPI.func1(arg.doubleValue());
        else if(arg instanceof Float) VendorAPI.func1(arg.floatValue());
        else VendorAPI.func1(arg.intValue());
    }
    static void func1(char arg) { VendorAPI.func1(arg); }
}

您还需要更改my_func的定义以限制类型参数,然后在其中,您可以VendorAPIWrapper.func1(arg)。问题是Character不是Number,因此,为了保证类型安全,您仍然需要两个版本的函数,一个用于数字,另一个用于字符,除非你愿意在调用之前将字符转换为整数或字节。

答案 5 :(得分:2)

我想你想要这样的东西:

static <T> void my_func(T arg) {
    // much of code, which uses T
   // ...
    if(arg instanceof Integer) {
        VendorApi.func1((Integer) arg)
    } else if(arg instanceof Double) {
          ...
    } else {
       throw new IllegalArgumentException("...");
    }  

答案 6 :(得分:1)

只需将您的方法拆分为通用部分和调用部分:

public class Main {
     static void my_func(char arg) {
       my_funcGenericPart(arg);
       VendorApi.func1(arg);
     }
     static void my_func(int arg) {
       my_funcGenericPart(arg);
       VendorApi.func1(arg);
     }
     static void my_func(float arg) {
       my_funcGenericPart(arg);
       VendorApi.func1(arg);
     }
     static void my_func(double arg) {
       my_funcGenericPart(arg);
       VendorApi.func1(arg);
     }
     private static <T> void my_funcGenericPart(T arg) {
             // much of code, which uses T
             // ...

             // the caller will invoke the right VendorApi.func1(arg);
     }

     public static void main(String args[]) {
             // call my_func for each type (char, int, float, double)
             // ...
             int i = 1;
             my_func(i);
             char c = 1;
             my_func(c);
     }
}

请注意,您的类型参数T在此处没有用,您可以简单地将通用方法的参数声明为Object(或Number),但没有区别。

这在概念上接近Alex’ answer但不需要Java 8.但是他的答案有一个优点,即通用代码可以选择在哪个位置调用VendorApi.func1,而这个简单的解决方案只允许调用在通用部分之前或之后......