Java泛型 - 使Generic可扩展2个接口 [英] Java generics - Make Generic to extends 2 interfaces

查看:132
本文介绍了Java泛型 - 使Generic可扩展2个接口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您如何使此工作:

  public class Frankenstein< T extends IHuman,IMonster> {
}

无需

  public interface Weirdo extends Ihuman,IMonster {
}



public< T> void mapThis(
Class< ;? extends MyClass< T>> key,Class< ;? extends T& IDisposable> value){

}

我收到编译器消息标记 Class<?延伸T& IDisposable> 为错误。

解决方案

Reimeus已经指出您要求您的编辑是不可能的。



你会认为你可以使用下面的方法:

  public< T,U extends T& IDisposable> void mapThis(
Class< ;? extends MyClass< T>> key,
Class< ;? extends U> value
){...}

事实上,这是我第一次看到这篇文章时的想法。但这实际上给出了编译器错误:


一个类型变量后面可能不会跟随其他边界


< blockquote>

为了帮助我解释为什么,我想引用一个 Oracle博客由Victor Rudometov发表关于此错误的帖子


这个事实并不总是清楚的,以下代码
不应编译:



interface I {}



class TestBounds< U,T extends U& I> {



}



因为JLS第4章
类型,值和变量部分4.4类型变量陈述:
绑定包含 类型变量或类或类接口类型
T可能后面跟着另外的接口类型I 1 ,...,I n 。所以一个
可以使用 T extends U,T extends SomeClass& I ,但不是 T extends U& I
此规则适用于所有情况,包括
方法和构造函数中的类型变量和范围。


此限制在一个紧密相关的帖子中进行了探讨:为什么我无法在带有多个边界的类型参数中使用类型参数?



总而言之,限制是为了排除某些尴尬的情况( JLS§4.9)。



什么样的尴尬情况? Chris Povirk的回答描述了一个:


[限制的原因是]指定非法类型的可能性。具体来说,使用不同的参数扩展通用接口两次。我不能想出一个非设计的例子,但:

  / **包含一个比较器< String>它也实现给定类型T. * / 
class StringComparatorHolder< T,C extends T&比较器< String>> {
private final C comparator;
// ...
}

void foo(StringComparatorHolder< Comparator< Integer> ;,?> holder){...}

现在 holder.comparator 是一个 Comparator< Integer> 比较器< String>


Chris还指向 Sun bug 4899305 ,这是一个针对此语言限制的错误。它被关闭作为未修复与以下注释:


如果类型变量可以后跟类型变量或通过b $ b parameterized)接口,可能会有更多的互相
递归类型变量,这是非常难以处理的。事情
已经复杂,当绑定只是一个参数化类型,
例如。 < S,R extends Comparable< S>< / code> ;.因此,边界现在不会改变
。 javac和Eclipse都同意 S& T
S& Comparable< S> / p>

所以这些是限制背后的原因。具体解决通用方法(你的问题关注),我想进一步指出,类型推论在理论上会导致这样的边界无论如何是无意义的。



如果我们重新检查上述假设签名中声明的类型参数:

 < T,U extends T& IDisposable> 

假设调用者没有明确指定 T U ,可以缩减为以下内容:

  ; T,U extends Object& IDisposable> 

或者这只是微妙的区别,但是另一主题):

 < T,U extends IDisposable> 

这是因为 T 任何边界,因此无论传递什么类型的参数, T 始终可以解析为 Object ,然后可以 U



让我们回去说 T 有界:

 < T extends Foo,U extends T& IDisposable> 

这可以以同样的方式减少( Foo 可以是类或接口):

 < T extends Foo,U extends Foo& IDisposable> 

至少基于理论上的推理,你尝试的语法实现是无意义的,直到限制调用者更具体的论点。我几乎可以在这里停止。 但是... 是您尝试执行的用例。 它与编译器推断通用方法类型参数的方式有关,这导致我的理论推理走出窗口。采取以下通用方法:

  class MyClass {
static< T& void foo(T t1,T t2){}
}

这是一个常见的初学者尝试创建一个需要两个参数相同类型的方法的错误。当然,由于继承的工作原理,这是无意义的:

  MyClass.foo(asdf,42) // legal 

这里, T Object - 这与之前关于简化 mapThis 类型参数的推理相符。您必须手动指定类型参数才能实现预期的类型检查:

  MyClass。< String> foo asdf,42); //编译器错误

进来,这是一个不同的事情与多个类型参数与交错边界:

  class MyClass {
static< T ,U延伸T> void foo(T t,U u){}
}

  MyClass.foo(asdf,42) //编译器错误

表已经改变了 - 我们必须手动放宽类型参数编译:

  MyClass。< Object,Object> foo(asdf,42); // legal 

这是因为编译器推断出方法类型参数的有限方式。因此,你想要实现的是实际上有一个应用程序限制调用者的参数。


How do you make this work:

public class Frankenstein<T extends IHuman, IMonster>{
}

Without making

public interface Weirdo extends Ihuman, IMonster{
}

Edit

Why is this not working?

public <T> void mapThis(
        Class<? extends MyClass<T>> key, Class<? extends T & IDisposable> value) {

}

I am getting compiler message marking Class<? extends T & IDisposable> as an Error.

解决方案

Reimeus already pointed out that what you're asking for in your edit isn't possible. I'd just like to expand a little on why.

One would think you could use the following:

public <T, U extends T & IDisposable> void mapThis(
        Class<? extends MyClass<T>> key,
        Class<? extends U> value
) { ... }

In fact that's what came to my mind when I first saw this post. But this actually gives a compiler error:

a type variable may not be followed by other bounds

To help me explain why, I'd like to quote an Oracle Blogs post by Victor Rudometov about this error:

This fact is not always clear, but it is true. The following code should not compile:

interface I {}

class TestBounds <U, T extends U & I> {

}

Because JLS Chapter 4 Types, Values, and Variables section 4.4 Type Variables states: "The bound consists of either a type variable, or a class or interface type T possibly followed by further interface types I1 , ..., In.". So one may use T extends U, T extends SomeClass & I, but not T extends U & I. This rule applies to all cases including type variables and bounds in methods and constructors.

The reasons for this restriction are explored in a closely related post: Why can't I use a type argument in a type parameter with multiple bounds?

To summarize, the restriction was imposed in order to "preclude certain awkward situations coming into existence" (JLS §4.9).

What kind of awkward situations? An answer by Chris Povirk describes one:

[A reason for the restriction is] the possibility of specifying illegal types. Specifically, extending a generic interface twice with different parameters. I can't come up with a non-contrived example, but:

/** Contains a Comparator<String> that also implements the given type T. */
class StringComparatorHolder<T, C extends T & Comparator<String>> {
  private final C comparator;
  // ...
}

void foo(StringComparatorHolder<Comparator<Integer>, ?> holder) { ... }

Now holder.comparator is a Comparator<Integer> and a Comparator<String>.

Chris also points to Sun bug 4899305, which was a bug contesting this language restriction. It was closed as Won't Fix with the following comment:

If a type variable could be followed by type variables or by (possibly parameterized) interfaces, there would likely be more mutually recursive type variables, which are very difficult to handle. Things are already complicated when a bound is simply a parameterized type, e.g. <S,R extends Comparable<S>>. Consequently, bounds are not going to change now. Both javac and Eclipse agree that S&T and S&Comparable<S> are illegal.

So those are the reasons behind the restriction. Addressing generic methods specifically (which your question concerns), I'd like to further point out that type inference would theoretically cause such bounds to be pointless anyway.

If we reexamine the type parameters declared in the hypothetical signature above:

<T, U extends T & IDisposable>

Assuming the caller isn't explicitly specifying T and U, this can be reduced to the following:

<T, U extends Object & IDisposable>

Or just this (subtle difference, but that's another topic):

<T, U extends IDisposable>

This is because T doesn't have any bounds, so no matter what type of arguments get passed in, T can always resolve to Object at the very least, and so then can U.

Let's go back and say T is bounded:

<T extends Foo, U extends T & IDisposable>

This can be reduced in the same way (Foo could be a class or interface):

<T extends Foo, U extends Foo & IDisposable>

Based on that theoretical reasoning at least, the syntax you're trying to achieve is pointless as far as restricting the caller to more specific arguments. I could almost stop here. But... there is a use case for what you're trying to do. It has to do with the way the compiler infers generic method type parameters, which causes my theoretical reasoning to go out the window. Take the following generic method:

class MyClass {
    static <T> void foo(T t1, T t2) { }
}

This is a common beginner's mistake of trying to make a method that takes two parameters of the "same type". Of course it's pointless because of the way inheritance works:

MyClass.foo("asdf", 42); //legal

Here, T is inferred to be Object - this matches up with earlier reasoning about simplifying the mapThis type parameters. You have to manually specify the type parameters in order to achieve the intended type checking:

MyClass.<String>foo("asdf", 42); //compiler error

However, and here's where your use case starts to come in, it's a different matter with multiple type parameters with staggered bounds:

class MyClass {
    static <T, U extends T> void foo(T t, U u) { }
}

Now this call errors:

MyClass.foo("asdf", 42); //compiler error

The tables have turned - we have to manually relax the type parameters to get it to compile:

MyClass.<Object, Object>foo("asdf", 42); //legal

This happens because of the limited way in which the compiler infers method type parameters. For this reason, what you wanted to achieve would've actually had an application in restricting the caller's arguments.

这篇关于Java泛型 - 使Generic可扩展2个接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆