java 泛型示例_Java泛型示例教程–泛型方法,类,接口

java 泛型示例

Java Genrics is one of the most important features introduced in Java 5.

Java Genrics是Java 5中引入的最重要的功能之一。

If you have been working on Java Collections and with version 5 or higher, I am sure that you have used it.

如果您一直在使用Java Collections并使用版本5或更高版本,那么我确定您已经使用过它。

Generics in Java with collection classes is very easy but it provides a lot more features than just creating the type of collection.

Java中具有集合类的泛型非常容易,但是它提供了比仅创建集合类型更多的功能。

We will try to learn the features of generics in this article. Understanding generics can become confusing sometimes if we go with jargon words, so I would try to keep it simple and easy to understand.

在本文中,我们将尝试学习泛型的功能。 如果我们使用专业术语,对泛型的理解有时会变得混乱,因此我将尽量保持其简单易懂。

1. Java中的泛型 (1. Generics in Java)

Generics was added in Java 5 to provide compile-time type checking and removing risk of ClassCastException that was common while working with collection classes. The whole collection framework was re-written to use generics for type-safety. Let’s see how generics help us using collection classes safely.

Java 5中添加了泛型,以提供编译时类型检查并消除使用集合类时常见的ClassCastException风险。 整个收集框架都进行了重写,以使用泛型进行类型安全。 让我们看看泛型如何帮助我们安全地使用集合类。

List list = new ArrayList();
list.add("abc");
list.add(new Integer(5)); //OK

for(Object obj : list){
	//type casting leading to ClassCastException at runtime
    String str=(String) obj; 
}

Above code compiles fine but throws ClassCastException at runtime because we are trying to cast Object in the list to String whereas one of the element is of type Integer. After Java 5, we use collection classes like below.

上面的代码可以很好地编译,但是在运行时抛出ClassCastException,因为我们试图将列表中的Object强制转换为String,而其中一个元素是Integer类型。 在Java 5之后,我们使用如下收集类。

List<String> list1 = new ArrayList<String>(); // java 7 ? List<String> list1 = new ArrayList<>(); 
list1.add("abc");
//list1.add(new Integer(5)); //compiler error

for(String str : list1){
     //no type casting needed, avoids ClassCastException
}

Notice that at the time of list creation, we have specified that the type of elements in the list will be String. So if we try to add any other type of object in the list, the program will throw compile-time error. Also notice that in for loop, we don’t need typecasting of the element in the list, hence removing the ClassCastException at runtime.

请注意,在创建列表时,我们已指定列表中元素的类型为String。 因此,如果我们尝试在列表中添加任何其他类型的对象,则该程序将引发编译时错误。 还要注意,在for循环中,我们不需要列表中元素的类型转换,因此在运行时删除了ClassCastException。

2. Java通用类 (2. Java Generic Class)

We can define our own classes with generics type. A generic type is a class or interface that is parameterized over types. We use angle brackets (<>) to specify the type parameter.

我们可以使用泛型类型定义自己的类。 泛型类型是通过类型进行参数化的类或接口。 我们使用尖括号(<>)来指定type参数。

To understand the benefit, let’s say we have a simple class as:

为了了解其好处,我们假设有一个简单的类:

package com.journaldev.generics;

public class GenericsTypeOld {

	private Object t;

	public Object get() {
		return t;
	}

	public void set(Object t) {
		this.t = t;
	}

        public static void main(String args[]){
		GenericsTypeOld type = new GenericsTypeOld();
		type.set("Pankaj"); 
		String str = (String) type.get(); //type casting, error prone and can cause ClassCastException
	}
}

Notice that while using this class, we have to use type casting and it can produce ClassCastException at runtime. Now we will use java generic class to rewrite the same class as shown below.

请注意,在使用此类时,我们必须使用类型转换,并且它可以在运行时产生ClassCastException。 现在,我们将使用Java通用类重写如下所示的相同类。

package com.journaldev.generics;

public class GenericsType<T> {

	private T t;
	
	public T get(){
		return this.t;
	}
	
	public void set(T t1){
		this.t=t1;
	}
	
	public static void main(String args[]){
		GenericsType<String> type = new GenericsType<>();
		type.set("Pankaj"); //valid
		
		GenericsType type1 = new GenericsType(); //raw type
		type1.set("Pankaj"); //valid
		type1.set(10); //valid and autoboxing support
	}
}

Notice the use of GenericsType class in the main method. We don’t need to do type-casting and we can remove ClassCastException at runtime. If we don’t provide the type at the time of creation, the compiler will produce a warning that “GenericsType is a raw type.

注意main方法中GenericsType类的使用。 我们不需要进行类型转换,并且可以在运行时删除ClassCastException。 如果我们在创建时未提供类型,则编译器将发出警告,“ GenericsType是原始类型。

References to generic type GenericsType<T> should be parameterized”. When we don’t provide the type, the type becomes Object and hence it’s allowing both String and Integer objects. But, we should always try to avoid this because we will have to use type casting while working on raw type that can produce runtime errors.

泛型类型GenericsType <T>的引用应参数化”。 当我们不提供类型时,该类型将成为Object ,因此它允许String和Integer对象。 但是,我们应该始终避免这种情况,因为在处理可能产生运行时错误的原始类型时,我们必须使用类型转换。

Tip: We can use 提示 :我们可以使用 @SuppressWarnings("rawtypes") annotation to suppress the compiler warning, check out @SuppressWarnings("rawtypes")注释来取消编译器警告,请参阅 java annotations tutorialJava注释教程

Also notice that it supports java autoboxing.

还要注意,它支持Java自动装箱

3. Java通用接口 (3. Java Generic Interface)

Comparable interface is a great example of Generics in interfaces and it’s written as:

可比接口是接口中泛型的一个很好的例子,它写为:

package java.lang;
import java.util.*;

public interface Comparable<T> {
    public int compareTo(T o);
}

In similar way, we can create generic interfaces in java. We can also have multiple type parameters as in Map interface. Again we can provide parameterized value to a parameterized type also, for example new HashMap<String, List<String>>(); is valid.

以类似的方式,我们可以在Java中创建通用接口。 我们也可以像在Map界面中那样具有多个类型参数。 同样,我们也可以为参数化类型提供参数化值,例如new HashMap<String, List<String>>(); 已验证。

4. Java通用类型 (4. Java Generic Type)

Java Generic Type Naming convention helps us understanding code easily and having a naming convention is one of the best practices of Java programming language. So generics also comes with its own naming conventions. Usually, type parameter names are single, uppercase letters to make it easily distinguishable from java variables. The most commonly used type parameter names are:

Java通用类型命名约定可帮助我们轻松理解代码,而使用命名约定是Java编程语言的最佳实践之一。 因此,泛型也带有自己的命名约定。 通常,类型参数名称是单个大写字母,以使其易于与Java变量区分开。 最常用的类型参数名称为:

  • E – Element (used extensively by the Java Collections Framework, for example ArrayList, Set etc.)

    E –元素(由Java Collections Framework广泛使用,例如ArrayList,Set等)
  • K – Key (Used in Map)

    K –键(在地图中使用)
  • N – Number

    N –数字
  • T – Type

    T –类型
  • V – Value (Used in Map)

    V –值(在地图中使用)
  • S,U,V etc. – 2nd, 3rd, 4th types

    S,U,V等–第二,第三,第四类型

5. Java通用方法 (5. Java Generic Method)

Sometimes we don’t want the whole class to be parameterized, in that case, we can create java generics method. Since the constructor is a special kind of method, we can use generics type in constructors too.

有时我们不希望整个类都被参数化,在这种情况下,我们可以创建java泛型方法。 由于构造函数是一种特殊的方法,因此我们也可以在构造函数中使用泛型类型。

Here is a class showing an example of a java generic method.

这是一个显示Java泛型方法示例的类。

package com.journaldev.generics;

public class GenericsMethods {

	//Java Generic Method
	public static <T> boolean isEqual(GenericsType<T> g1, GenericsType<T> g2){
		return g1.get().equals(g2.get());
	}
	
	public static void main(String args[]){
		GenericsType<String> g1 = new GenericsType<>();
		g1.set("Pankaj");
		
		GenericsType<String> g2 = new GenericsType<>();
		g2.set("Pankaj");
		
		boolean isEqual = GenericsMethods.<String>isEqual(g1, g2);
		//above statement can be written simply as
		isEqual = GenericsMethods.isEqual(g1, g2);
		//This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets.
		//Compiler will infer the type that is needed
	}
}

Notice the isEqual method signature showing syntax to use generics type in methods. Also, notice how to use these methods in our java program. We can specify type while calling these methods or we can invoke them like a normal method. Java compiler is smart enough to determine the type of variable to be used, this facility is called type inference.

注意isEqual方法签名显示了在方法中使用泛型类型的语法。 另外,请注意如何在我们的Java程序中使用这些方法。 我们可以在调用这些方法时指定类型,也可以像普通方法一样调用它们。 Java编译器足够聪明,可以确定要使用的变量的类型,这种功能称为类型推断

6. Java泛型绑定类型参数 (6. Java Generics Bounded Type Parameters)

Suppose we want to restrict the type of objects that can be used in the parameterized type, for example in a method that compares two objects and we want to make sure that the accepted objects are Comparables. To declare a bounded type parameter, list the type parameter’s name, followed by the extends keyword, followed by its upper bound, similar like below method.

假设我们想限制可以在参数化类型中使用的对象的类型,例如,在比较两个对象的方法中,并且我们要确保接受的对象是可比较的。 要声明一个有界的类型参数,请列出该类型参数的名称,然后是extends关键字,然后是其上限,类似于下面的方法。

public static <T extends Comparable<T>> int compare(T t1, T t2){
		return t1.compareTo(t2);
	}

The invocation of these methods is similar to unbounded method except that if we will try to use any class that is not Comparable, it will throw compile-time error.

这些方法的调用与无界方法类似,不同之处在于,如果我们尝试使用任何非Comparable的类,都会引发编译时错误。

Bounded type parameters can be used with methods as well as classes and interfaces.

绑定类型参数可以与方法以及类和接口一起使用。

Java Generics supports multiple bounds also, i.e <T extends A & B & C>. In this case, A can be an interface or class. If A is class then B and C should be an interface. We can’t have more than one class in multiple bounds.

Java泛型也支持多个范围,即<T扩展A&B&C>。 在这种情况下,A可以是接口或类。 如果A是类,则B和C应该是接口。 在多个范围内,我们不能有多个类。

7. Java泛型和继承 (7. Java Generics and Inheritance)

We know that Java inheritance allows us to assign a variable A to another variable B if A is subclass of B. So we might think that any generic type of A can be assigned to generic type of B, but it’s not the case. Let’s see this with a simple program.

我们知道,如果A是B的子类,则Java继承允许我们将变量A分配给另一个变量B。因此,我们可能认为可以将A的任何泛型类型分配给B的泛型类型,但事实并非如此。 让我们用一个简单的程序看看。

package com.journaldev.generics;

public class GenericsInheritance {

	public static void main(String[] args) {
		String str = "abc";
		Object obj = new Object();
		obj=str; // works because String is-a Object, inheritance in java
		
		MyClass<String> myClass1 = new MyClass<String>();
		MyClass<Object> myClass2 = new MyClass<Object>();
		//myClass2=myClass1; // compilation error since MyClass<String> is not a MyClass<Object>
		obj = myClass1; // MyClass<T> parent is Object
	}
	
	public static class MyClass<T>{}

}

We are not allowed to assign MyClass<String> variable to MyClass<Object> variable because they are not related, in fact MyClass<T> parent is Object.

我们不允许将MyClass <String>变量分配给MyClass <Object>变量,因为它们不相关,实际上MyClass <T>的父对象是Object。

8. Java通用类和子类型 (8. Java Generic Classes and Subtyping)

We can subtype a generic class or interface by extending or implementing it. The relationship between the type parameters of one class or interface and the type parameters of another are determined by the extends and implements clauses.

我们可以通过扩展或实现来泛型一个通用类或接口。 一个类或接口的类型参数与另一类或接口的类型参数之间的关系由extend和Implements子句确定。

For example, ArrayList<E> implements List<E> that extends Collection<E>, so ArrayList<String> is a subtype of List<String> and List<String> is subtype of Collection<String>.

例如,ArrayList <E>实现扩展Collection <E>的List <E>,因此ArrayList <String>是List <String>的子类型,而List <String>是Collection <String>的子类型。

The subtyping relationship is preserved as long as we don’t change the type argument, below shows an example of multiple type parameters.

只要不更改type参数,子类型关系就会保留,下面显示了多个type参数的示例。

interface MyList<E,T> extends List<E>{
}

The subtypes of List<String> can be MyList<String,Object>, MyList<String,Integer> and so on.

List <String>的子类型可以是MyList <String,Object>,MyList <String,Integer>等。

9. Java通用通配符 (9. Java Generics Wildcards)

Question mark (?) is the wildcard in generics and represent an unknown type. The wildcard can be used as the type of a parameter, field, or local variable and sometimes as a return type. We can’t use wildcards while invoking a generic method or instantiating a generic class. In the following sections, we will learn about upper bounded wildcards, lower bounded wildcards, and wildcard capture.

问号(?)是泛型中的通配符,表示未知类型。 通配符可以用作参数,字段或局部变量的类型,有时还可以用作返回类型。 在调用通用方法或实例化通用类时,我们不能使用通配符。 在以下各节中,我们将学习上界通配符,下界通配符和通配符捕获。

9.1)Java泛型上界通配符 (9.1) Java Generics Upper Bounded Wildcard)

Upper bounded wildcards are used to relax the restriction on the type of variable in a method. Suppose we want to write a method that will return the sum of numbers in the list, so our implementation will be something like this.

上限通配符用于放松方法中对变量类型的限制。 假设我们要编写一个将返回列表中数字总和的方法,那么我们的实现将是这样的。

public static double sum(List<Number> list){
		double sum = 0;
		for(Number n : list){
			sum += n.doubleValue();
		}
		return sum;
	}

Now the problem with above implementation is that it won’t work with List of Integers or Doubles because we know that List<Integer> and List<Double> are not related, this is when an upper bounded wildcard is helpful. We use generics wildcard with extends keyword and the upper bound class or interface that will allow us to pass argument of upper bound or it’s subclasses types.

现在,上述实现的问题在于它不适用于List of Integers或Doubles,因为我们知道List <Integer>和List <Double>不相关,这在使用上限通配符很有用时。 我们将泛型通配符与extends关键字和上限类或接口一起使用,这将允许我们传递上限或其子类类型的参数。

The above implementation can be modified like the below program.

可以像下面的程序一样修改上面的实现。

package com.journaldev.generics;

import java.util.ArrayList;
import java.util.List;

public class GenericsWildcards {

	public static void main(String[] args) {
		List<Integer> ints = new ArrayList<>();
		ints.add(3); ints.add(5); ints.add(10);
		double sum = sum(ints);
		System.out.println("Sum of ints="+sum);
	}

	public static double sum(List<? extends Number> list){
		double sum = 0;
		for(Number n : list){
			sum += n.doubleValue();
		}
		return sum;
	}
}

It’s similar like writing our code in terms of interface, in the above method we can use all the methods of upper bound class Number. Note that with upper bounded list, we are not allowed to add any object to the list except null. If we will try to add an element to the list inside the sum method, the program won’t compile.

就像按照接口编写代码一样,在上述方法中,我们可以使用上限类Number的所有方法。 请注意,对于上界列表,除null之外,我们不允许将任何对象添加到列表中。 如果我们尝试在sum方法内将元素添加到列表中,则该程序将无法编译。

9.2)Java泛型无限制通配符 (9.2) Java Generics Unbounded Wildcard)

Sometimes we have a situation where we want our generic method to be working with all types, in this case, an unbounded wildcard can be used. Its same as using <? extends Object>.

有时,我们希望通用方法适用于所有类型,在这种情况下,可以使用无界通配符。 与使用<?相同 扩展Object>。

public static void printData(List<?> list){
		for(Object obj : list){
			System.out.print(obj + "::");
		}
	}

We can provide List<String> or List<Integer> or any other type of Object list argument to the printData method. Similar to upper bound list, we are not allowed to add anything to the list.

我们可以为printData方法提供List <String>或List <Integer>或任何其他类型的Object列表参数。 与上限列表类似,我们不允许在列表中添加任何内容。

9.3)Java泛型下界通配符 (9.3) Java Generics Lower bounded Wildcard)

Suppose we want to add Integers to a list of integers in a method, we can keep the argument type as List<Integer> but it will be tied up with Integers whereas List<Number> and List<Object> can also hold integers, so we can use a lower bound wildcard to achieve this. We use generics wildcard (?) with super keyword and lower bound class to achieve this.

假设我们要在方法中将整数添加到整数列表中,我们可以将参数类型保持为List <Integer>,但它将与Integers捆绑在一起,而List <Number>和List <Object>也可以容纳整数,因此我们可以使用下限通配符来实现。 我们使用带有super关键字和下限类的泛型通配符(?)来实现此目的。

We can pass lower bound or any supertype of lower bound as an argument, in this case, java compiler allows to add lower bound object types to the list.

我们可以传递下界或下界的任何超类型作为参数,在这种情况下,java编译器允许将下界对象类型添加到列表中。

public static void addIntegers(List<? super Integer> list){
		list.add(new Integer(50));
	}

10.使用泛型通配符进行子类型化 (10. Subtyping using Generics Wildcard)

List<? extends Integer> intList = new ArrayList<>();
List<? extends Number>  numList = intList;  // OK. List<? extends Integer> is a subtype of List<? extends Number>

11. Java泛型类型擦除 (11. Java Generics Type Erasure)

Generics in Java was added to provide type-checking at compile time and it has no use at run time, so java compiler uses type erasure feature to remove all the generics type checking code in byte code and insert type-casting if necessary. Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

添加了Java泛型以在编译时提供类型检查,并且在运行时没有使用,因此Java编译器使用类型擦除功能来删除字节码中的所有泛型类型检查代码,并在必要时插入类型转换。 类型擦除可确保不会为参数化类型创建新的类; 因此,泛型不会产生运行时开销。

For example, if we have a generic class like below;

例如,如果我们有如下通用类;

public class Test<T extends Comparable<T>> {

    private T data;
    private Test<T> next;

    public Test(T d, Test<T> n) {
        this.data = d;
        this.next = n;
    }

    public T getData() { return this.data; }
}

The Java compiler replaces the bounded type parameter T with the first bound interface, Comparable, as below code:

Java编译器用第一个绑定接口Comparable替换了绑定类型参数T,如下代码:

public class Test {

    private Comparable data;
    private Test next;

    public Node(Comparable d, Test n) {
        this.data = d;
        this.next = n;
    }

    public Comparable getData() { return data; }
}

12.泛型常见问题解答 (12. Generics FAQs)

12.1)为什么我们在Java中使用泛型? (12.1) Why do we use Generics in Java?)

Generics provide strong compile-time type checking and reduces risk of ClassCastException and explicit casting of objects.

泛型提供了强大的编译时类型检查,并降低了ClassCastException和显式对象转换的风险。

12.2)泛型中的T是什么? (12.2) What is T in Generics?)

We use <T> to create a generic class, interface, and method. The T is replaced with the actual type when we use it.

我们使用<T>创建通用类,接口和方法。 我们在使用T时将其替换为实际类型。

12.3)泛型如何在Java中工作? (12.3) How does Generics work in Java?)

Generic code ensures type safety. The compiler uses type-erasure to remove all type parameters at the compile time to reduce the overload at runtime.

通用代码确保类型安全。 编译器使用类型擦除在编译时删除所有类型参数,以减少运行时的重载。

13. Java泛型–进一步阅读 (13. Generics in Java – Further Readings)

That’s all for generics in java, java generics is a really vast topic and requires a lot of time to understand and use it effectively. This post here is an attempt to provide basic details of generics and how can we use it to extend our program with type-safety.

这是所有的Java泛型 ,Java泛型是一个非常庞大的题目,需要大量的时间来了解和有效地使用它。 本文旨在提供泛型的基本细节,以及如何使用泛型来扩展程序的类型安全性。

翻译自: https://www.journaldev.com/1663/java-generics-example-method-class-interface

java 泛型示例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值