简体   繁体   English

捕获的读/写操作? 在爪哇

[英]Read/write operations with capture of ? in Java

I have a generic class with type T, that has both read a write methods using T. Let's illustrate it on List.我有一个类型为 T 的泛型类,它具有使用 T 的读取和写入方法。让我们在 List 上进行说明。

When I use it with a wildcard type, I come across a specific problem:当我将它与通配符类型一起使用时,我遇到了一个特定的问题:

List<?> list = ...
list.add(list.get(0)); //here I get a compiler error

As I understand it, Java compiler should internally store the type as "capture of ?"据我了解,Java 编译器应在内部将类型存储为“捕获?” and it should recognize, that the types used in add/get match.它应该认识到 add/get 中使用的类型匹配。

If this is impossible to do, what is the "nicest" workaround?如果这是不可能的,那么“最好的”解决方法是什么? My thoughts:我的想法:

  1. Helper methods辅助方法

    private <S> void aaa(List<S> list) { list.add(list.get(0)); }

    This works, but it would be impractical to have these methods for all possible combinations.这可行,但是将这些方法用于所有可能的组合是不切实际的。

  2. Casting铸件

    List<?> list; private <S> void aaa() { List<S> list = (List<S>)this.list list.add(list.get(0)); }

    This works as well, but I don't like it either, plus I get an unchecked cast warning.这也有效,但我也不喜欢它,而且我收到未经检查的演员表警告。

  3. Creating type创建类型

    class Foo<T> { List<T> list; private void aaa() { list.add(list.get(0)); } }

    This works too, but it seems like a total overkill to define a type just for this purpose.这也有效,但仅仅为此目的定义一个类型似乎完全是矫枉过正。

Thanks for your thoughts!谢谢你的想法!

As I understand it, Java compiler should internally store the type as "capture of ?"据我了解,Java 编译器应在内部将类型存储为“捕获?” and it should recognize, that the types used in add/get match.它应该认识到 add/get 中使用的类型匹配。

That's not what the capturing system does.这不是捕获系统所做的。 Unfortunately.很遗憾。

this works but it would be impractical to have these methods for all possible combinations这行得通,但是将这些方法用于所有可能的组合是不切实际的

'all possible combinations'? '所有可能的组合'? There's just the one combination, really.真的只有一种组合。 Or rather, the amount of times you perform such an interaction, where you feed the result of invoking a method on thing directly as parameter to another method on thing isn't exactly a common task.或者更确切地说,您执行这种交互的次数,在这种交互中,您将在事物上调用方法的结果直接作为参数提供给事物上的另一个方法,这并不是一项常见的任务。 You'd have to write a method each time it happens, but, given that its rare, I don't see how that is impractical.每次发生时您都必须编写一个方法,但是,鉴于它很少见,我不认为这是不切实际的。 Note that you can, of course, make that 0 a parameter if you like.请注意,如果您愿意,当然可以将该0作为参数。

Creating type创建类型

This is a really bad idea.这是一个非常糟糕的主意。 Not because its overkill.不是因为它矫枉过正。 Because it leaks.因为它会泄漏。 Your generics are public .您的泛型是public You'd want this aspect of it to be hidden.您希望隐藏它的这一方面。 Or rather, the semantics of your type, whatever it might be, either involves including the type or not - and 'I have a need to copy an element in the list to the end of that list' should play absolutely no part whatsoever in your design decision about whether or not the type itself should have generics or not to represent the type of this list field/parameter you get.或者更确切地说,你的类型的语义,无论它可能是什么,要么包括类型,要么不包括 - 并且“我需要将列表中的元素复制到该列表的末尾”绝对不应该在你的关于类型本身是否应该具有泛型的设计决策来表示您获得的此列表字段/参数的类型。

what is the "nicest" workaround?什么是“最好的”解决方法?

Pretty much the helper method, I think.我认为几乎是辅助方法。 Note that you if you really really want to, you can shove it in a local:请注意,如果您真的想要,可以将其推到本地:

import java.util.*;

class Test {
  public static void main(String[] args) {
    List<String> a = new ArrayList<String>();
    a.add("Hello"); a.add("World");
    copyFirstToEnd(a);
    System.out.println(a);
  }

  private static void copyFirstToEnd(List<?> in) {
    class Helper {
      <S> void help(List<S> a) { a.add(a.get(0)); }
    }

    new Helper().help(in);
  }
}

works great.效果很好。 Unwieldy;笨重; code is kinda ugly.代码有点丑。 But absolutely nothing whatsoever can 'see' this other than your own eyeballs and only when explicitly looking at the code that powers this method.但是除了你自己的眼球之外,绝对没有任何东西可以“看到”这一点,并且只有在明确查看支持此方法的代码时。 You won't see it in your outline.你不会在大纲中看到它。 You won't see it in your javadoc.你不会在你的 javadoc 中看到它。 You won't see it in your auto-complete dialogs (other than when editing code inside copyFirstToEnd where that's a good thing), and of course anybody working on a source file that isn't this Test.java itself cannot even know you've made a type for this, even if they try to employ reflection*.你不会在你的自动完成对话框中看到它(除了copyFirstToEnd中编辑代码时,这是一件好事),当然,任何处理不是这个Test.java本身的源文件的人甚至都不知道你已经为此做了一个类型,即使他们尝试使用反射*。

*) They can hack around and find the Test$Helper.class artefact but at this point that feels academic. *) 他们可以四处寻找并找到Test$Helper.class人工制品,但在这一点上感觉很学术。 That class screams in all possible ways that you shouldn't consider it as existing in the first place, and tooling and IDEs do indeed act exactly like that (keeping it entirely out of type finding dialogs and the like).该类以所有可能的方式尖叫,您不应该首先将其视为存在,并且工具和 IDE 确实确实如此(使其完全脱离类型查找对话框等)。

It's a shame you get an object alloc but it's got a single ref field at worst, and is thread-localized fast garbage.很遗憾你得到了一个对象 alloc,但它在最坏的情况下只有一个 ref 字段,并且是线程本地化的快速垃圾。 It's as cheap an alloc as you could possibly make, so the cost is probably effectively nil, given java's generation garbage collection systems and clear focus on supporting 'lots of small immutable objects' as a coding model fairly well.它是尽可能便宜的分配器,因此成本可能实际上为零,因为 java 的生成垃圾收集系统和明确关注支持“大量小的不可变对象”作为编码模型相当好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM