草庐IT

Java嵌套泛型类型

coder 2023-05-17 原文

为什么必须使用泛型类型 Map<?, ? extends List<?>>而不是更简单的 Map<?, List<?>>对于以下 test()方法?

public static void main(String[] args) {
    Map<Integer, List<String>> mappy =
        new HashMap<Integer, List<String>>();

    test(mappy);
}

public static void test(Map<?, ? extends List<?>> m) {}

// Doesn't compile
// public static void test(Map<?, List<?>> m) {}

请注意以下工作,并且无论如何这三种方法具有相同的删除类型。
public static <E> void test(Map<?, List<E>> m) {}

最佳答案

从根本上说,List<List<?>>List<? extends List<?>>有不同的类型参数。

实际上,一个是另一个的子类型,但首先让我们详细了解它们各自的含义。

理解语义差异

一般来说,通配符?代表一些“缺失的信息”。这意味着“这里曾经有一个类型参数,但我们不再知道它是什么”。并且因为我们不知道它是什么,所以对我们如何使用引用该特定类型参数的任何内容施加了限制。

目前,让我们使用 List 来简化示例。而不是 Map .

  • A List<List<?>>拥有任何类型参数的任何类型的 List。所以即:
    List<List<?>> theAnyList = new ArrayList<List<?>>();
    
    // we can do this
    theAnyList.add( new ArrayList<String>() );
    theAnyList.add( new LinkedList<Integer>() );
    
    List<?> typeInfoLost = theAnyList.get(0);
    // but we are prevented from doing this
    typeInfoLost.add( new Integer(1) );
    

    我们可以放任何 ListtheAnyList ,但这样做我们已经失去了对它们元素的了解。
  • 当我们使用 ? extends , List拥有 List 的一些特定子类型,但我们不再知道它是什么。所以即:
    List<? extends List<Float>> theNotSureList =
        new ArrayList<ArrayList<Float>>();
    
    // we can still use its elements
    // because we know they store Float
    List<Float> aFloatList = theNotSureList.get(0);
    aFloatList.add( new Float(1.0f) );
    
    // but we are prevented from doing this
    theNotSureList.add( new LinkedList<Float>() );
    

    theNotSureList 添加任何内容不再安全,因为我们不知道其元素的实际类型。 (它最初是 List<LinkedList<Float>> 吗?还是 List<Vector<Float>> ?我们不知道。)
  • 我们可以把这些放在一起,得到一个 List<? extends List<?>> .我们不知道是什么类型的 List它已经在里面了,我们不知道那些 List 的元素类型要么。所以即:
    List<? extends List<?>> theReallyNotSureList;
    
    // these are fine
    theReallyNotSureList = theAnyList;
    theReallyNotSureList = theNotSureList;
    
    // but we are prevented from doing this
    theReallyNotSureList.add( new Vector<Float>() );
    // as well as this
    theReallyNotSureList.get(0).add( "a String" );
    

    我们丢失了有关 theReallyNotSureList 的信息,以及 List 的元素类型在里面。

    (但您可能会注意到,我们可以将任何类型的 List 持有 Lists 分配给它...)

  • 所以要分解它:
    //   ┌ applies to the "outer" List
    //   ▼
    List<? extends List<?>>
    //                  ▲
    //                  └ applies to the "inner" List
    
    Map工作方式相同,只是有更多的类型参数:
    //  ┌ Map K argument
    //  │  ┌ Map V argument
    //  ▼  ▼
    Map<?, ? extends List<?>>
    //                    ▲
    //                    └ List E argument
    

    为什么? extends是必要的

    你可能知道"concrete"泛型类型具有不变性,即 List<Dog> is not a subtype of List<Animal> 即使 class Dog extends Animal .相反,通配符是我们如何获得协方差,即 List<Dog>List<? extends Animal> 的子类型.
    // Dog is a subtype of Animal
    class Animal {}
    class Dog extends Animal {}
    
    // List<Dog> is a subtype of List<? extends Animal>
    List<? extends Animal> a = new ArrayList<Dog>();
    
    // all parameterized Lists are subtypes of List<?>
    List<?> b = a;
    

    因此,将这些想法应用于嵌套 List :
  • List<String>List<?> 的子类型但是 List<List<String>>不是 List<List<?>> 的子类型.如前所述,这可以防止我们通过向 List 添加错误的元素而危及类型安全。 .
  • List<List<String>>List<? extends List<?>> 的子类型,因为有界通配符允许协方差。即,? extends允许 List<String> 的事实是 List<?> 的子类型被考虑。
  • List<? extends List<?>>实际上是一个共享父类(super class)型:
         List<? extends List<?>>
              ╱          ╲
    List<List<?>>    List<List<String>>
    

  • 审核中
  • Map<Integer, List<String>>只接受 List<String>作为一个值。
  • Map<?, List<?>>接受任何 List作为一个值。
  • Map<Integer, List<String>>Map<?, List<?>>是具有不同语义的不同类型。
  • 一个不能转换为另一个,以防止我们以不安全的方式进行修改。
  • Map<?, ? extends List<?>>是一个共享父类(super class)型,它施加了安全限制:
            Map<?, ? extends List<?>>
                 ╱          ╲
    Map<?, List<?>>     Map<Integer, List<String>>
    


  • 泛型方法的工作原理

    通过在方法上使用类型参数,我们可以断言 List有一些具体的类型。
    static <E> void test(Map<?, List<E>> m) {}
    

    此特定声明要求所有 List s 在 Map具有相同的元素类型。我们不知道该类型实际上是什么,但我们可以以抽象的方式使用它。这允许我们执行“盲”操作。

    例如,这种声明可能对某种累积有用:
    static <E> List<E> test(Map<?, List<E>> m) {
        List<E> result = new ArrayList<E>();
    
        for(List<E> value : m.values()) {
            result.addAll(value);
        }
    
        return result;
    }
    

    我们无法拨打 putm因为我们不知道它的 key 类型是什么了。但是,我们可以操纵它的值,因为我们知道它们都是 List具有相同的元素类型。

    只是为了踢

    问题未讨论的另一个选项是为 List 使用有界通配符和泛型类型。 :
    static <E> void test(Map<?, ? extends List<E>> m) {}
    

    我们可以用类似 Map<Integer, ArrayList<String>> 的东西来调用它。 .如果我们只关心 E 的类型,这是最宽松的声明.

    我们还可以使用边界来嵌套类型参数:
    static <K, E, L extends List<E>> void(Map<K, L> m) {
        for(K key : m.keySet()) {
            L list = m.get(key);
            for(E element : list) {
                // ...
            }
        }
    }
    

    这既允许我们可以传递给它的内容,也允许我们如何操作 m以及其中的一切。

    也可以看看
  • "Java Generics: What is PECS?" ? extends之间的区别和 ? super .
  • JLS 4.10.2. Subtyping among Class and Interface TypesJLS 4.5.1. Type Arguments of Parameterized Types获取此答案的技术细节的入口点。
  • 关于Java嵌套泛型类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22806202/

    有关Java嵌套泛型类型的更多相关文章

    1. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

      我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

    2. ruby - 将散列转换为嵌套散列 - 2

      这道题是thisquestion的逆题.给定一个散列,每个键都有一个数组,例如{[:a,:b,:c]=>1,[:a,:b,:d]=>2,[:a,:e]=>3,[:f]=>4,}将其转换为嵌套哈希的最佳方法是什么{:a=>{:b=>{:c=>1,:d=>2},:e=>3,},:f=>4,} 最佳答案 这是一个迭代的解决方案,递归的解决方案留给读者作为练习:defconvert(h={})ret={}h.eachdo|k,v|node=retk[0..-2].each{|x|node[x]||={};node=node[x]}node[

    3. ruby - Infinity 和 NaN 的类型是什么? - 2

      我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

    4. ruby - 检查方法参数的类型 - 2

      我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

    5. java - 等价于 Java 中的 Ruby Hash - 2

      我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

    6. ruby - Ruby 有 `Pair` 数据类型吗? - 2

      有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

    7. Ruby——嵌套类和子类是一回事吗? - 2

      下面例子中的Nested和Child有什么区别?是否只是同一事物的不同语法?classParentclassNested...endendclassChild 最佳答案 不,它们是不同的。嵌套:Computer之外的“Processor”类只能作为Computer::Processor访问。嵌套为内部类(namespace)提供上下文。对于ruby​​解释器Computer和Computer::Processor只是两个独立的类。classComputerclassProcessor#Tocreateanobjectforthisc

    8. ruby - 模块嵌套代码风格偏好 - 2

      我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

    9. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

      我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

    10. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

      我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

    随机推荐