- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
让我们首先考虑一个简单的场景( see complete source on ideone.com ):
import java.util.*;
public class TwoListsOfUnknowns {
static void doNothing(List<?> list1, List<?> list2) { }
public static void main(String[] args) {
List<String> list1 = null;
List<Integer> list2 = null;
doNothing(list1, list2); // compiles fine!
}
}
doNothing
与
List<String>
和一个
List<Integer>
.换句话说,两个
?
可以指完全不同的类型。因此,以下内容无法编译,这是意料之中的(
also on ideone.com ):
import java.util.*;
public class TwoListsOfUnknowns2 {
static void doSomethingIllegal(List<?> list1, List<?> list2) {
list1.addAll(list2); // DOES NOT COMPILE!!!
// The method addAll(Collection<? extends capture#1-of ?>)
// in the type List<capture#1-of ?> is not applicable for
// the arguments (List<capture#2-of ?>)
}
}
import java.util.*;
public class LOLUnknowns1 {
static void probablyIllegal(List<List<?>> lol, List<?> list) {
lol.add(list); // this compiles!! how come???
}
}
sun-jdk-1.6.0.17
上为我编译在 ideone.com 中,但应该吗?我们不可能有
List<List<Integer>> lol
和一个
List<String> list
,来自
TwoListsOfUnknowns
的类似的两个不相关通配符情况?
import java.util.*;
public class LOLUnknowns2 {
static void rightfullyIllegal(
List<List<? extends Number>> lol, List<?> list) {
lol.add(list); // DOES NOT COMPILE! As expected!!!
// The method add(List<? extends Number>) in the type
// List<List<? extends Number>> is not applicable for
// the arguments (List<capture#1-of ?>)
}
}
import java.util.*;
public class LOLUnknowns3 {
static void probablyIllegalAgain(
List<List<? extends Number>> lol, List<? extends Number> list) {
lol.add(list); // compiles fine!!! how come???
}
}
List<List<Integer>> lol
和一个
List<Float> list
,所以这不应该编译,对吧?
LOLUnknowns1
(两个无界通配符)并尝试查看我们是否实际上可以调用
probablyIllegal
以任何方式。让我们先尝试“简单”的情况,并为两个通配符选择相同的类型(
as seen on ideone.com ):
import java.util.*;
public class LOLUnknowns1a {
static void probablyIllegal(List<List<?>> lol, List<?> list) {
lol.add(list); // this compiles!! how come???
}
public static void main(String[] args) {
List<List<String>> lol = null;
List<String> list = null;
probablyIllegal(lol, list); // DOES NOT COMPILE!!
// The method probablyIllegal(List<List<?>>, List<?>)
// in the type LOLUnknowns1a is not applicable for the
// arguments (List<List<String>>, List<String>)
}
}
List<List<Integer>> lol
和
List<String> list
也给出了类似的编译错误!事实上,根据我的实验,代码编译的唯一方法是第一个参数是显式
null
类型(
as seen on ideone.com ):
import java.util.*;
public class LOLUnknowns1b {
static void probablyIllegal(List<List<?>> lol, List<?> list) {
lol.add(list); // this compiles!! how come???
}
public static void main(String[] args) {
List<String> list = null;
probablyIllegal(null, list); // compiles fine!
// throws NullPointerException at run-time
}
}
LOLUnknowns1
,
LOLUnknowns1a
和
LOLUnknowns1b
:
probablyIllegal
接受? lol.add(list);
完全编译?它是类型安全的吗? import java.util.*;
public class DoubleLOL {
static void omg2xLOL(List<List<?>> lol1, List<List<?>> lol2) {
// compiles just fine!!!
lol1.addAll(lol2);
lol2.addAll(lol1);
}
}
import java.util.*;
public class IntoTheWild {
public static void main(String[] args) {
List<?> list = new ArrayList<String>(); // compiles fine!
List<List<?>> lol = new ArrayList<List<String>>(); // DOES NOT COMPILE!!!
// Type mismatch: cannot convert from
// ArrayList<List<String>> to List<List<?>>
}
}
List<List<String>>
不是
List<List<?>>
.事实上,虽然任何
List<E>
是
List<?>
,它看起来不像任何
List<List<E>>
是
List<List<?>>
(
as seen on ideone.com ):
import java.util.*;
public class IntoTheWild2 {
static <E> List<?> makeItWild(List<E> list) {
return list; // compiles fine!
}
static <E> List<List<?>> makeItWildLOL(List<List<E>> lol) {
return lol; // DOES NOT COMPILE!!!
// Type mismatch: cannot convert from
// List<List<E>> to List<List<?>>
}
}
List<List<?>>
?
最佳答案
如附录 B 所示,这与多个通配符无关,而是误解了 List<List<?>>
真正意思。
让我们首先提醒自己 Java 泛型是不变的意味着什么:
Integer
是 Number
List<Integer>
不是 List<Number>
List<Integer>
是 List<? extends Number>
List<String>
是(可被捕获)List<?>
List<List<String>>
不是(可被捕获)List<List<?>>
List<List<String>>
IS(可被捕获)List<? extends List<?>>
List<List<?>>
这样的类型。可以捕获类似
List<List<String>>
的类型,
List<List<Integer>>
等。这不是真的。
List<List<?>>
:
List<? extends List<?>>
List<List<?>> lolAny = new ArrayList<List<?>>();
lolAny.add(new ArrayList<Integer>());
lolAny.add(new ArrayList<String>());
// lolAny = new ArrayList<List<String>>(); // DOES NOT COMPILE!!
List<? extends List<?>> lolSome;
lolSome = new ArrayList<List<String>>();
lolSome = new ArrayList<List<Integer>>();
List<List<? extends Number>> lolAnyNum = new ArrayList<List<? extends Number>>();
lolAnyNum.add(new ArrayList<Integer>());
lolAnyNum.add(new ArrayList<Float>());
// lolAnyNum.add(new ArrayList<String>()); // DOES NOT COMPILE!!
// lolAnyNum = new ArrayList<List<Integer>>(); // DOES NOT COMPILE!!
List<? extends List<? extends Number>> lolSomeNum;
lolSomeNum = new ArrayList<List<Integer>>();
lolSomeNum = new ArrayList<List<Float>>();
// lolSomeNum = new ArrayList<List<String>>(); // DOES NOT COMPILE!!
public class LOLUnknowns1d {
static void nowDefinitelyIllegal(List<? extends List<?>> lol, List<?> list) {
lol.add(list); // DOES NOT COMPILE!!!
// The method add(capture#1-of ? extends List<?>) in the
// type List<capture#1-of ? extends List<?>> is not
// applicable for the arguments (List<capture#3-of ?>)
}
public static void main(String[] args) {
List<Object> list = null;
List<List<String>> lolString = null;
List<List<Integer>> lolInteger = null;
// these casts are valid
nowDefinitelyIllegal(lolString, list);
nowDefinitelyIllegal(lolInteger, list);
}
}
lol.add(list);
是非法的,因为我们可能有
List<List<String>> lol
和一个
List<Object> list
.事实上,如果我们注释掉有问题的语句,代码就会编译,这正是我们在
main
中的第一次调用时所做的。 .
probablyIllegal
问题中的方法不违法。它们都是完全合法且类型安全的。编译器绝对没有错误。它正在做它应该做的事情。
List<Animal> animals = new ArrayList<Dog>()
? (This was brought up in the first revision of the answer; it's a worthy supplement to the type invariant argument.)
5.1.10 Capture Conversion
Let G name a generic type declaration with n formal type parameters A1…An with corresponding bounds U1…Un. There exists a capture conversion from G<T1…Tn> to G<S1…Sn>, where, for 1 <= i <= n:
- If Ti is a wildcard type argument of the form
?
then …- If Ti is a wildcard type argument of the form
? extends
Bi, then …- If Ti is a wildcard type argument of the form
? super
Bi, then …- Otherwise, Si = Ti.
Capture conversion is not applied recursively.
?
可以抄送;这取决于它出现的位置。规则 4 中没有递归应用,但是当规则 2 或 3 应用时,相应的 Bi 本身可能是 CC 的结果。
List<?>
可以抄送List<String>
?
可以按规则 1 抄送 List<? extends Number>
可以抄送List<Integer>
?
可以按规则 2 抄送 Number
List<? extends Number>
不能抄送 List<String>
?
可以按规则 2 CC,但由于类型不兼容而导致编译时错误 List<List<?>>
不能抄送 List<List<String>>
?
不能抄送 List<? extends List<?>>
可以抄送List<List<String>>
?
可以按规则 2 抄送 List<?>
, 可以抄送 List<String>
?
可以抄送List<? extends List<? extends Number>>
可以抄送List<List<Integer>>
?
可以按规则 2 抄送 List<? extends Number>
, 可以抄送 List<Integer>
?
可以抄送List<? extends List<? extends Number>>
不能抄送 List<List<Integer>>
?
可以按规则 2 抄送 List<? extends Number>
,它可以 CC,但在应用于 List<Integer>
时会出现编译时错误?
可以抄送?
可以 CC 而其他人不能,请考虑以下规则:您不能直接实例化通配符类型。也就是说,以下给出了编译时错误:
// WildSnippet1
new HashMap<?,?>(); // DOES NOT COMPILE!!!
new HashMap<List<?>, ?>(); // DOES NOT COMPILE!!!
new HashMap<?, Set<?>>(); // DOES NOT COMPILE!!!
但是,以下编译得很好:
// WildSnippet2
new HashMap<List<?>,Set<?>>(); // compiles fine!
new HashMap<Map<?,?>, Map<?,Map<?,?>>>(); // compiles fine!
原因
WildSnippet2
编译是因为,如上所述,没有
?
可以抄送。在
WildSnippet1
, 要么
K
或
V
(或两者)
HashMap<K,V>
can CC,通过
new
直接实例化非法的。
关于java - 泛型方法上的多个通配符使 Java 编译器(和我!)非常困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3546745/
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!