Java对象返回类型与通用方法

时间:2016-04-07 13:18:30

标签: java generics

我看到了几个关于泛型返回类型的问题,但没有人回答我的问题。
如果没有任何参数的约束,例如JayWay中的以下方法:

public static <T> T read(String json, String jsonPath, Filter... filters) {
    return new JsonReader().parse(json).read(jsonPath, filters);
}

使用它作为通用有什么意义?
我告诉我的团队成员,这个方法应该用作:

JsonPath.<Boolean>read(currentRule, "$.logged")

而不是:

(boolean) JsonPath.read(currentRule, "$.logged")

但我真的无法区分......

4 个答案:

答案 0 :(得分:8)

泛型工作由编译器将不可见的强制转换插入到代码中。

例如,在将泛型添加到语言之前,您必须执行此操作。

List list = new ArrayList();
list.add("Foo");
list.add("Bar");
String str0 = (String) list.get(0);
String str1 = (String) list.get(1);

这非常烦人。由于get()返回了Object,因此您必须每次都希望从String投放List

如今,List是通用的,get()返回T,所以你可以这样做。

List<String> list = new ArrayList<>();
list.add("Foo");
list.add("Bar");
String str0 = list.get(0);
String str1 = list.get(1);

这里发生的事情是编译器通过为你添加演员阵容将新版本变成旧版本,但他们仍然存在。

但是,泛型的全部意义在于这些编译器生成的强制转换保证是安全的 - 即它们可能无法在运行时抛出ClassCastException

在我看来,如果你使用泛型来隐藏那些保证安全的演员,只是因为他们很烦人,那就是滥用这个功能。

它是否是通用方法而你

Boolean a = JsonPath.<Boolean>read(currentRule, "$.logged");

或者它会返回Object而你会

Boolean a = (Boolean) JsonPath.read(currentRule, "$.logged");

两个版本都可以在运行时抛出ClassCastException,所以如果你被迫投射,我认为它会更好,至少你知道你已经知道了#{1}}。做一些可能失败的事情。

我认为泛型方法的返回类型涉及类型参数T是不好的做法,如果方法参数没有,除非返回的对象不能以某种方式使用这会危及类型安全。例如,

public static <T> List<T> emptyList()
Collections中的

是正常的(列表为空,因此它不能包含错误类型的元素)。

在您的情况下,我认为read方法不应该是通用的,只应返回Object

答案 1 :(得分:5)

我远离

的主要原因
JsonPath.<Boolean>read(currentRule, "$.logged")

是内部执行未经检查的强制转换,并隐藏此事实。例如,您可以在同一个地方调用此方法:

JsonPath.<String>read(currentRule, "$.logged")

并且你无法知道那里可能存在问题,直到它在运行时实际发生 - 它仍然会编译,你甚至都没有得到警告。

没有离开那些无法控制的演员 - 我只是在代码中把它放在我面前,所以我知道存在潜在的危险;这使我能够采取合理的措施来缓解这个问题。

@SuppressWarnings("unchecked")  // I know something might go wrong here!
boolean value = (boolean) JsonPath.read(currentRule, "$.logged")

答案 2 :(得分:1)

两者之间没有任何功能上的差异。字节码可能完全相同。

核心区别在于,当otrher使用泛型时,使用强制转换。

如果有任何替代机制,我通常会尽量避免施放,因为通用形式是一种非常有效的替代方案,我会为此而努力。

// The right way.
JsonPath.<Boolean>read(currentRule, "$.logged");

答案 3 :(得分:1)

具有从未设置的类型参数(在调用JsonPath.read(currentRule, "$.logged")时),实际上使编译器完全忽略方法中的所有通用信息,并将所有类型参数替换为:

  • Object,如果type-parameter没有上限。 (就像你的情况一样)
  • U,如果type-parameter的界限类似于<T extends U>。例如,如果您将<T extends Number>作为类型参数并通过调用JsonPath.read(...)忽略它,那么编译器将使用Number替换type-parameter。

在使用强制转换((boolean) JsonPath.read(...))的情况下,type-parameter将替换为Object。然后,通过首先返回boolean(可能),然后将此包装器自动取消装箱到Boolean,将此类型无法安全地转换为boolean。这根本不安全。实际上,每个演员阵容都不安全 - 你告诉编译器很多:“我知道这个类型在运行时是什么类型,所以请相信我,让我把它转换成与它兼容的其他东西。”你的谦卑仆人,编译器允许这样做,但如果你错了,那就不安全了。 :)

你的方法也有另外一件事。 type-parameter永远不会在方法体或参数中使用 - 这使得它非常冗余。由于通过对boolean执行强制转换,您坚持要知道new JsonReader().parse(json).read(jsonPath, filters);的返回类型,那么您应该只返回boolean(或Boolean):

public static Boolean read(String json, String jsonPath, Filter... filters) {
    return new JsonReader().parse(json).read(jsonPath, filters);
}