为特定类型的类声明String到Class的Map

时间:2014-03-03 16:27:14

标签: java generics reflection collections jar

我正在尝试围绕其他所有具有main方法的类执行命令行包装器类。

我已经向类声明了一个字符串映射,但现在我想确保所有类都实现一个特定的接口。

这个想法是你给一个别名别名或“命令名称”作为第一个参数,然后我会调用该类的主要部分并将其余的argv数组传递给它。

最初我有这个:

static final Map<String, Class> CLASSES = new LinkedHashMap<String,Class>() {{
    put( "command1", com.foobar.package_a.ClassOne.class   );
    put( "command2", com.foobar.package_a.ClassTwo.class   );
    put( "command3", com.foobar.package_b.ClassThree.class );
    ...
}};

但是我想让用户在没有args的情况下运行命令并列出类和它们的作用。从以前的工作中,大多数课程都有描述。

我决定在界面中将其正式化:

public interface HasDescription {
    String getDescription();
}

然后:

public class ClassOne implements HasDescription {
    // I'd prefer static, but that's a different concern
    public String getDescription() {
        return "check files for gophers";
    }
    ... rest of class ...
}

但我不知道如何宣布它,我有很多变化。以下是工作的一个示例:

static final Map<String, Class<T implements HasDescription>> CLASSES = new LinkedHashMap<String,Class<T implements HasDescription>>() {{
    put( "command1", com.foobar.package_a.ClassOne.class   );
    put( "command2", com.foobar.package_a.ClassTwo.class   );
    put( "command3", com.foobar.package_b.ClassThree.class );
}};

变化/教训:

  • 我尝试过在尖括号内没有前导字母
  • 我已经尝试了implementsextends
  • 即使在Java 7中,您也不能在声明内联时使用new LinkedHashMap<>()

问题:

  • 理想情况下.getDescription()将是静态的。
  • 我假设如果我有正确的签名,然后用.get()将其拉出哈希,我将分配给一个类似声明的变量(没有顶级Mop&lt;&gt;)
  • 如果拉出一个合格的类变量,是否有一个快捷方式或特殊版本的反射,让我说一些像((HasDescription)clazz).getDescription()?
  • 想知道我是重新发明轮子,还是有一些原因这是一个非常糟糕的主意
  • 指定所有类必须实现特定接口是否在此使用场景中为我买了什么?至少,它似乎让编译器为我做了一些检查。在运行时,我想知道获得一个合格的类引用是否比常规反射具有特殊的优势。

为什么我这样做?

  • 我正在让Maven制作一个完整的,自包含的.jar文件
  • 我希望大家只用java -jar my_jar.jar运行它...所以我需要选择一个特定的类;你将使用java -jar projname.jar command_name arg1 arg2 arg3
  • 运行
  • 如果您只使用java -jar projname.jar运行,则会获得一个类列表及其执行的操作
  • 另一种方法是将每个命令作为java -cp projname.jar com.foobar.package_a.ClassOne arg1 arg2 arg3运行,但这对于输入的人来说有点难看,并且每个类名都需要.sh和.bat。

2 个答案:

答案 0 :(得分:4)

您不使用类似的类型参数。类型参数可以在类级别方法级别中声明。使用这样的字段声明,您必须使用通配符:

static final Map<String, Class<? extends HasDescription>> CLASSES = new LinkedHashMap<String,Class<? extends HasDescription>>() {{
    put( "command1", com.foobar.package_a.ClassOne.class   );
    put( "command2", com.foobar.package_a.ClassTwo.class   );
    put( "command3", com.foobar.package_b.ClassThree.class );
}};
  

我已经尝试了implementsextends

在泛型中,您只需使用extends关键字来表示这两个概念。

  

理想情况下.getDescription()将是静态的。

不,你不能那样做。这不会给你目前想要的多态行为。

  

如果拉出一个合格的类变量,是否有一个快捷方式或特殊版本的反射,只是让我说( (HasDescription)clazz ).getDescription()之类的东西?

如果要调用这样的方法,那么为什么要存储Class实例而不是实例本身?如果要存储Class实例,则必须使用Class#newInstance()方法对其进行实例化,并确保所有实现类都为此提供0-arg构造函数。

  

指定所有类必须实现特定接口是否在这种使用场景中为我买了什么?

如果您要存储Class实例,那么您必须这样做。但是如果您自己存储实例,那么只需将引用保留为HasDescription就可以了:

static final Map<String, HasDescription> map = new LinkedHashMap<>();
map.put("command1", new com.foobar.package_a.ClassOne());
map.put("command2", new com.foobar.package_a.ClassTwo());

然后你可以这样做:

map.get("command1").getDescription();

答案 1 :(得分:1)

您不会在字符串中将接口声明为类映射。您可以通过使用以下内容来查看特定类是否实现了接口:

Class<?> testClass = [ lookup your class, or test class before adding to  your map];
boolean isProperInterface = HasDescription.class.isAssignableFrom(testClass);

(在不同情况下,您可以使用

检查对象是否是类或接口的实例
 boolean isGood = object instanceof [class or interface name]