转换和泛型如何工作?

How does casting and Generics work?

在阅读了一些示例之后,我仍然不太明白什么是泛型。 到目前为止我找到的最好的解释是 What does <> (angle brackets) mean in Java?

据我了解,如果您使用泛型,则无需将 return 值转换为类型。

但是泛型实际上做了什么?据我了解,确保(?)returned 值已转换为括号中定义的类型。

但这意味着,Java 需要对每个赋值进行强制转换。

String str = (String) otherstring;

是正确的,

String str = otherstring;

不合适。

据我了解,泛型 <String> 会确保值在赋值之前进行转换,所以我们不必转换它?

是否 Java 需要转换每个值,即使它已经是相同的类型?

转换和使用泛型之间的区别在于,它们是一种编译时机制。意思是,下面的代码不会得到运行时异常,而是根本无法编译:

Clazz <T> c = new Clazz <U> ();

如果 T 不能转换为 U。

PHP 在某种意义上更容易,但这只是因为它不关心其中的一些问题。使用 PHP,您稍后会处理这些问题。

投射基本上是在两个方向之一(向上或向下)。向上转换表明您更愿意仅通过它继承自(或继承其接口)的 class 的接口来使用该对象。

例如,如果我有一个 FileInputStream,我可以将其更改为 "just" 一个 InputStream

FileInputStream fileInput = ...
InputStream input = (InputStream)fileInput;

请注意,这对可访问方法的实现的影响完全为零;但有时确实会隐藏以前可用的方法。

fileInput.getFileName();  // let's pretend this works
input.getFileName(); // this shouldn't work, as any InputStream doesn't have a file name.

向下转换是不同的(而且很危险)。你基本上声明即使你有一个 InputStream,你也知道它应该被视为一个 FileInputStream

InputStream input2 = ...;
FileInputStream fileInput2 = (FileInputStream)input2;

如果这种情况下的 input2 没有合适的 FileInputStream 类型,那么您将在 运行 时得到一个 class 转换异常。如果它确实具有适当的类型,则会分配 fileInput2。

使用这种类型系统的主要原因是您可以轻松编写可重用的组件。 "higher" 类型(向上转换类型)指定通用合同,而 "lower" 类型(向下转换类型)指定特定细节,这些细节在其超类型指定的通用合同中有所不同。

泛型;然而,是不同的蜡球。基本上在处理 Collections 以及与 Collections 相关的事物时,规则是相似的(但由于多种原因必须有所不同)。

泛型行为不同的原因之一是向后兼容性。将 "extra" 类型信息添加到已经存在的类型(如 java.util.List)的设计目标意味着对于泛型和非泛型的混合使用,该类型实际上不能在 运行 时存在。在 运行 时间不被认为存在的泛型类型 属性 称为 "erasure"。简而言之,当你写

List<Student> students = new ArrayList<Student>();

你正在有效地编译

List students = new ArrayList();

但是您的编译器会做 "extra" 工作以确保在您编写的任何源代码中,您只能添加一个 "Student" 对象。同样,当您阅读整个列表时,您不需要强制转换为 "Student" 对象,因为编译器会假定列表中只能包含 "Student" 个对象。

请注意,要让这样的系统运行,它只是编译时。这意味着它们不适用类型层次结构和 运行 时间转换的详细信息。这反映在泛型添加的新类型约束中。

T extends Student

表示 T 可以转换为 Student(向上转换)

T super Student

表示 T 是 Student 的超 class 后一个很棘手,但在某些情况下很有用。

如果CollegeStudent、HighSchoolStudent、GradeSchoolStudent都继承自Student,那么这三种Student都可以存储在一个List

如果需要保证一个 Collection 至少 提供 Students,你可以给出一个 List.

public void studentProcessor(Collection<? super Student> students);

可以处理 List、List、List

通用 class 为您可以想象的任何对象提供功能 - 这个 处理的 class 有时称为您的 参数。例如 java.util.List 接口及其实现 classes,例如ArrayList<E> 为任意类型 E 提供列表功能(添加元素、删除元素、元素计数等...)(它可用于管理您自己类型的列表 Maroun) .泛型类型的实现必须独立于它处理的参数。在示例中 - List 独立于 Maroun

所以泛型的概念与类型转换几乎没有任何关系,如您所见;)

当您有一个 class 实例可以通过 type 参数化时使用泛型。常见的例子是集合。

在泛型出现之前,Collection 包含许多 class 化的对象,Objects。虽然这些对象有可能具有编译器无法识别的更具体的类型,因此需要危险的运行时转换为实际类型。

现在您可以参数化 Collection<E>,其中 E 是元素类型。现在编译器可以确保 Collection<Foo> 只添加了 Foo 个实例,并且它可以 return 键入 Foo 个实例而无需强制转换。