[英]What can we do with generics in Java to make them look better:
我有这种方法可以使用列表元素的属性之一将List
转换为Map
:
简而言之,它看起来像这样:
private Map<String, List<Diagnostic<? extends JavaFileObject>>> toMap( List<Diagnostic<? extends JavaFileObject>> diagnostics ) {
Map<String, List<Diagnostic<? extends JavaFileObject>>> result = new HashMap<String, List<Diagnostic<? extends JavaFileObject>>>();
for ( Diagnostic<? extends JavaFileObject> d : diagnostics ) {
List<Diagnostic<? extends JavaFileObject>> list = null;
if ( !result.containsKey( d.getCode() ) ) {
list = new ArrayList<Diagnostic<? extends JavaFileObject>>();
result.put( d.getCode(), list );
} else {
list = result.get( d.getCode() );
}
assert list != null;
list.add( d );
}
return result;
}
亚克...
我非常喜欢泛型,我在它们之前使用 java 并且我不想 go 回到铸造一切时代,但是当泛型包含作为元素时,它本身就是一个泛型元素,事情 Z34D1F91FB2E5514B8576FAB 混乱。
我知道在 Java1.7 中我们将能够使用“菱形”运算符,但应该有另一种方式。
这是在非通用版本中的样子:
private Map toMap( List diagnostics ) {
Map result = new HashMap();
for( Object o : diagnostics ) {
Diagnostic d = ( Diagnostic ) o;
List list = null;
if( !result.containsKey( d.getCode() ) ) {
list = new ArrayList();
result.put( d.getCode() , list );
} else {
list = result.get( d.getCode() );
}
assert list != null;
list.add( d );
}
return result;
}
大约,我没有尝试编译它。
其他语言如何处理这个? 例如 C#?Scala? 我非常喜欢 SML 或 Haskell 处理的方式,但我认为太多的魔法可能会伤害(但这当然是主观的)
有解决方法吗?
您定义一个名为T
的类型参数。 然后你可以像这样在你的泛型中使用T
:
private <T extends JavaFileObject> Map<String, List<Diagnostic<T>> toMap(List<Diagnostic<T> diagnostics) {
Map<String, List<Diagnostic<T>> result = new HashMap<String, List<Diagnostic<T>>();
for (Diagnostic<T> d : diagnostics ) {
List<Diagnostic<T>> list = null;
if ( !result.containsKey(d.getCode())) {
list = new ArrayList<Diagnostic<T>>();
result.put( d.getCode(), list );
} else {
list = result.get( d.getCode() );
}
assert list != null;
list.add( d );
}
return result;
}
在上面,您将看到定义为<T extends JavaFileObject>
的类型参数,并且您可以在需要的任何地方重用 T。 这将使它更清洁一些。
在 Scala 中,这看起来像:
// collections are immutable by default, but we want the mutable flavour
import collection.mutable
// An alias so we don't keep repeating ourself
type DiagMultiMap[T] = mutable.Map[String, mutable.Set[Diagnostic[T]]]
//pimp DiagMultiMap with the addDiagnostic method
class MapDiag[T](theMap: DiagMultiMap[T]) {
def addDiagnostic(d: Diagnostic[T]): Unit = {
val set = theMap.getOrElseUpdate(d.getCode) {mutable.Set.empty}
set += d
}
}
//an implicit conversion to enable the pimp
implicit def mapDiagPimp[T](theMap: DiagMultiMap[T]) = new MapDiag(theMap)
//This is how we make one
def mkDiagnosticMultiMap[T](entries: Seq[Diagnostic[T]]): DiagMultiMap[T] = {
val theMap = new mutable.HashMap[String, mutable.Set[Diagnostic[T]]]()
entries foreach { theMap addDiagnostic _ }
theMap
}
它未经测试,因为我无法访问Diagnostic
代码
更新
这会教我在深夜发帖,实际上要容易得多...
给定任何Diagnostic
对象序列:
val diags = List(new Diagnostic(...), new Diagnositic(...), ...)
它们可以很容易地用一种方法进行分组:
val diagMap = diags.groupBy(_.getCode)
但它比这更复杂一点!
一个更大的问题是Diagnostic
是 Java 标准库的一部分,所以你不能用方差注释重写它(在代码之后更多)。 不过,包装器可以解决问题,幸运的是它不是太大:
class RichDiagnostic[S+](underlying: Diagnostic[S]) {
def code: String = underlying.getCode
def columnNumber: Long = underlying.getColumnNumber
def endPosition: Long = underlying.getEndPosition
def kind: Diagnostic.Kind = underlying.getKind
def lineNumber: Long = underlying.getLineNumber
def messageFor(locale: Locale): String = underlying.getMessage(locale)
def position: Long = underlying.getPosition
def source: S = underlying.getSource
def startPosition: Long = underlying.getStartPosition
implicit def toUnderlying: Diagnostic[S] = underlying
}
[S+]
+
的 + 将此 class 标记为协变,因此如果A
是B
的子类,则RichDiagnostic[A]
被认为是RichDiagnostic[B]
的子类。 这是避免讨厌的通用签名的关键,不再是<? extends T>
<? extends T>
或<? super T>
<? super T>
!
它也很容易使用:
val richDiags = diags.map(d => new RichDiagnostic(d))
val diagMap = richDiags.groupBy(_.code)
如果诊断程序最初是作为 Java 列表提供的,那么像map
这样的方法将不会自动提供给您,但转换很简单:
import collection.JavaConverters._
//the toList isn't strictly necessary, but we get a mutable Buffer otherwise
val richDiags = diagsFromJava.asScala.toList.map(d => new RichDiagnostic(d))
val diagMap = richDiags.groupBy(_.code)
构建此集合是一次性操作,如果将条目添加到基础列表中,则必须重复此操作,但我怀疑这不会成为问题。
很好的例子。 在通用版中,有19种arguments; 在原始版本中,只有 1 个演员。 由于这只是一种私有方法,因此我将 go 与原始版本一起使用。 即使它更公开,它仍然可以保留原始方法体,但具有完整的通用签名。 大概是这样的
Map<String, List<Diagnostic<? extends JavaFileObject>>>
toMap( List<Diagnostic<? extends JavaFileObject>> diagnostics )
{
Map result = new HashMap();
for( Diagnostic d : diagnostics )
{
List list = (List)result.get( d.getCode() );
if(list==null)
result.put( d.getCode(), list=new ArrayList());
list.add( d );
}
return result;
}
通过更一般的签名输入和 Java 7,我们可以有
<D extends Diagnostic<?>>
Map<String, List<D>> toMap( List<D> diagnostics )
{
Map<String, List<D>> result = new HashMap<>();
for( D d : diagnostics )
{
List<D> list = result.get( d.getCode() );
if(list==null)
result.put( d.getCode(), list=new ArrayList<>());
list.add( d );
}
return result;
}
void test()
{
List<Diagnostic<? extends JavaFileObject>> x = null;
Map<String, List<Diagnostic<? extends JavaFileObject>>> map = toMap(x);
}
8 型 arguments。
就我个人而言,我会尝试破坏这样的东西(Eclipse 已编译 - 未尝试运行)
private class MapDiag extends HashMap<String, List<Diagnostic<? extends JavaFileObject>>>{
private static final long serialVersionUID = 1L;
void add(Diagnostic<? extends JavaFileObject> d){
List<Diagnostic<? extends JavaFileObject>> list = null;
if (containsKey(d.getCode())){
list = get(d.getCode());
}
else {
list = new ArrayList<Diagnostic<? extends JavaFileObject>>();
put( d.getCode(), list );
}
list.add(d);
}
}
private MapDiag toMap2( List<Diagnostic<? extends JavaFileObject>> diagnostics ) {
MapDiag result = new MapDiag();
for ( Diagnostic<? extends JavaFileObject> d : diagnostics ) {
result.add(d);
}
return result;
}
我认为这里的一些评论已经得出了“答案”,但我认为到目前为止还没有人给出规范的表述。
private <T extends Diagnostic<? extends JavaFileObject>>
Map<String, List<T>> toMap(List<T> diagnostics) {
Map<String, List<T>> result = new HashMap<String, List<T>>();
for (T d : diagnostics) {
List<T> list = null;
if (!result.containsKey(d.getCode())) {
list = new ArrayList<T>();
result.put(d.getCode(), list);
} else {
list = result.get(d.getCode());
}
assert list != null;
list.add(d);
}
return result;
}
类型参数的引入极大地简化了方法的内部,同时保持了签名的表现力。
应该注意的是,这与提出的问题不同,但总的来说可能更正确。 此处给出的方法的区别将确保该方法的输入和 output 的诊断参数化类型相同。
不幸的是,在这种情况下,两个构造函数的调用阻止了我们进一步使用类型参数(特别是对于 Map),尽管如果我们愿意允许自己进行强制转换,我们可以使该方法更加简洁。
首先,你的方法是不是错了?......我的意思是,它不应该更像
List<T> list = null;
if (!result.containsKey(d.getCode())) {
list = newArrayList();
} else {
list = result.get(d.getCode());
}
result.put(d.getCode(), list);
此外,您始终可以使用 static 实用程序方法来模拟菱形运算符,从而为您提供某种类型的推断。 也就是说
public static <K, V> HashMap<K, V> newHashMap() {
return new HashMap<K, V>();
}
public static <T> ArrayList<T> newArrayList() {
return new ArrayList<T>();
}
然后你的方法看起来像
private Map<String, List<Diagnostic<? extends JavaFileObject>>> toMap(List<Diagnostic<? extends JavaFileObject>> diagnostics) {
Map<String, List<Diagnostic<? extends JavaFileObject>>> result = newHashMap();
for (Diagnostic<? extends JavaFileObject> d : diagnostics) {
List<Diagnostic<? extends JavaFileObject>> list = null;
if (!result.containsKey(d.getCode())) {
list = newArrayList();
result.put(d.getCode(), list);
} else {
list = result.get(d.getCode());
}
assert list != null;
list.add(d);
}
return result;
}
至少实例会更小......请注意,如果您使用的是 google guava 库,您可能已经拥有此实用程序方法。 如果你把它和窗帘狗给你的答案结合起来,你会得到
private <T extends Diagnostic<? extends JavaFileObject>> Map<String, List<T>> toMap(List<T> diagnostics) {
Map<String, List<T>> result = newHashMap();
for (T d : diagnostics) {
List<T> list = null;
if (!result.containsKey(d.getCode())) {
list = newArrayList();
result.put(d.getCode(), list);
} else {
list = result.get(d.getCode());
}
assert list != null;
list.add(d);
}
return result;
}
将每个人的建议混搭起来,这就是我所做的:
我创建了一个新的 class DiagnosticList
列表来包装ArrayList<Diagnostic<? extends JavaFileObject>>
ArrayList<Diagnostic<? extends JavaFileObject>>
这很简单:
static final class DiagnosticList
extends ArrayList<Diagnostic<? extends JavaFileObject>>{
// no arg constructor
public DiagnosticList(){}
// Using a list
public DiagnosticList(List<Diagnostic<? extends JavaFileObject>> diagnostics){
super( diagnostics);
}
}
然后我可以取消方法签名。
private Map<String, DiagnosticList> toMap( DiagnosticList diagnostics ) {
Map<String, DiagnosticList> result = new HashMap<String, DiagnosticList>();
for ( Diagnostic<? extends JavaFileObject> d : diagnostics ) {
DiagnosticList list = result.get(d.getCode());
if( list == null ) {
result.put( d.getCode(), (list = new DiagnosticList()));
}
list.add( d );
}
return result;
}
这很容易阅读。
虽然我可能会更改原始程序的语义,但我认为我将受益于可维护性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.