Java generic class method parameter

How to write generic classes and methods in Java

This tutorial helps you write your own generic stuffs i.e. generic classes and generic methods in Java.

Why should we write generic code?

The reason I tell you to write generic code is that generic code helps you save time and effort, in terms of avoiding redundancy and creating more reusable and more general API.

Keep reading and you will see the power and robustness of writing generic code.

1. Writing Generic Classes in Java

Suppose that we are developing a database library called Data Access Object (DAO) for a program that manages resources in a university. We would write a DAO class for managing Students like the following code:

public class StudentDAO < public void save(Student student) < // code to save student details to database >public Student get(long id) < // code to get student details from database. // . and return a Student object >>

Next, we need to persist Professor objects to database. We would write another DAO class like this:

public class ProfessorDAO < public void save(Professor professor) < // code to save professor details to database >public Professor get(long id) < // code to get professor details from database. // . and return a Professor object >>

Hey, is this ProfessorDAO class similar to the StudentDAO class, isn’t it? What if new entity classes added to the system: class, course, faculty, etc? It looks like we will continue creating DAO classes whose code is almost identical. This creates redundancy and needs time to write similar classes each time a new entity added to the system.

So how will generics help us to avoid this redundancy and time-wasting?

public class GeneralDAO  < public void save(T entity) < // code to save entity details to database >public T get(long id) < // code to get details from database. // . and return a T object >>

Here, T is called type parameter of the GeneralDAO class. T stands for any type. The GeneralDAO class is said to be generified. The following code illustrates how to use this generic class:

GeneralDAO studentDAO = new GeneralDAO(); Student newStudent = new Student(); studentDAO.save(newStudent); Student oldStudent = studentDAO.get(250);

With this generic class, we can manage other entities easily without writing additional DAO classes, for example:

GeneralDAO professorDAO = new GeneralDAO(); Professor newProfessor = new Professor(); professorDAO.save(newProfessor); Professor oldProfessor = professorDAO.get(100);

As you have seen, we can write one generic class that works with different types. This removes redundancy and makes the code more readable, more reusable and more general. Also, when a new type is added, the generic class can be reused without writing additional code, thus saving times.

Here are some noteworthy points with regards to writing generic classes in Java:

  • T is just a name for a type parameter, like a variable name. That means you can use any name you like for the type parameter. However, there are some conventions for naming type parameters in Java: T for type; E for element; K for key; V for value, etc. And we should follow this convention.
  • The type parameter can be used as an actual type in the body of the class. As you see in the GeneralDAO class, T is used as parameter type in the save() method and as a return type in the get() method.
  • Type parameters are never added to the names of constructors or methods. They are used only in the names of classes and interfaces.
Читайте также:  Calling methods in class java

* Writing generic classes with more than one type parameter

Just like the generic Map , we declare more than one type parameter when generifying a class. The type parameters are separated by commas.

For example, the following class is declared with two type parameters to represent a pair of things:

public class Pair  < T first; U second; public Pair(T first, U second) < this.first = first; this.second = second; >public T getFirst() < return first; >public U getSecond() < return second; >>

* Using bounded type parameters when writing generic classes

In the above examples, we use the type parameter T which can be of any type. How can we restrict the type parameter to be subtypes of a concrete type? For example, we want to design the GeneralDAO class to work with only types that are subtypes of the Entity class (suppose Entity is the base type of all entities in the system). That means no one can declare GeneralDAO or GeneralDAO which we don’t expect.

Fortunately, Java generics provide the concept bounded type parameters that allow us to use the syntax to specify restrictions on definition of generic classes ( X is a concrete type).

So we modify the GeneralDAO class as shown below:

public class GeneralDAO  < public void save(T entity) < // code to save entity details to database >public T get(long id) < // code to get details from database. // . and return a T object >>

Here, Entity is called the upper bound, which can be any class or interface. Remember the extends keyword is used for both class and interface in the cased of bounded type parameter.

Now, with this bounded type definition, the GeneralDAO class can be used only work with sub types of Entity , not with every type. Hence the following code becomes illegal:

GeneralDAO dao = new GeneralDAO(); dao.save(new Integer());

Let’s see some more examples.

The following class is generified to work with only Swing components ( JComponent is supertype of all Swing components):

public class Box  < private Listlist; public void add(T comp) < list.add(comp); >>

And the following generic class is a restricted version of a map. The key is restricted to only Number types, and value is restricted to only Runnable types:

public class MapThread  < private Mapmap = new HashMap(); public void put(T num, U thread) < map.put(num, thread); >>

* Using multiple bounds

We can use the syntax to define a generic class whose type parameter can be sub types of multiple types. Here, X , Y , Z can be classes and interfaces. Remember if there is a class, the class must be the first in the list.

For example, the following generic class is designed works with only types that are sub types of Runnable and JFrame :

Читайте также:  Вывод данных

I recommend you to read the book Java Generics and Collections by Maurice Naftalin and Philip Wadler to understand deeply about generics in Java.

2. Writing Generic Methods

Like generic classes, we can write generic methods that are highly general and reusable. There are also some differences when writing generic methods.

First, let’s see how a non-generic method is converted to a generic one.

The following method counts number of occurrences of a String in an array of Strings:

public static int count(String[] array, String item) < int count = 0; if (item == null) < for (String s : array) < if (s == null) count++; >> else < for (String s : array) < if (item.equals(s)) < count++; >> > return count; >
String[] helloWorld = ; int count = count(helloWorld, "l"); System.out.println("#occurrences of l: " + count);

Now, we need to count the occurrence of an element in an array of any type. To generify this method, replace the concrete type String with a type parameter T , hence the generic version looks like this:

public static int count(T[] array, T item) < int count = 0; if (item == null) < for (T element : array) < if (element == null) count++; >> else < for (T element : array) < if (item.equals(element)) < count++; >> > return count; >
Integer[] integers = ; int count = count(integers, 0); System.out.println("#occurrences of zeros: " + count);

— The is always placed before the return type of the method. It indicates that the T identifier is a type parameter, to distinguish it with concrete types.

— Of course we can use any name for the type parameter. However, T is a convention in Java and we should follow.

— Note that if the type parameter of a non-static generic method is same as the enclosing class, the indicator is not required. The following class illustrates this point:

The following code shows you the differences between the type parameters in the class and in the method:

public class Util  < public static int count(T[] array, T item) < // code. >>

* Using bounded type parameters in generic methods:

Like generic classes, we can use bounded type parameters to restrict the types which can be accepted by the generic method.

Let’s refactor the count() method above to work with a collection instead of an array like this:

public static int count(Collection col, T item) < int count = 0; if (item == null) < for (T element : col) < if (element == null) count++; >> else < for (T element : col) < if (item.equals(element)) < count++; >> > return count; >

The following code shows how to use the bounded type parameter to update the method to accept only collection of sub types of Number :

public static int count(Collection col, T item) < // code. >

Here, Number is upper bound of the type parameter T . Remember the upper bound can be a class or an interface or both (the extends keyword is also used for interface here).

List integers = Arrays.asList(0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1); int count = count(integers, 1); System.out.println("#occurrences of 1s: " + count);

The following code shows the count() method now accepts only types that are sub types of JComponent (class) and Runnable (interface):

public int count(Collection col, T item)

public static void paring(T first, U second) < Mapmap = new HashMap(); map.put(first, second); >

* Using type wildcards in generic methods:

Do you remember the generic wildcards and which you learned in the tutorial Generics with extends and super Wildcards and the Get and Put Principle? I mean that you can also use wildcards as same as bounded types. The count() method above can be rewritten using wildcards instead of bounded types like this:

public static int count(Collection col, Number item) < int count = 0; if (item == null) < for (Number element : col) < if (element == null) count++; >> else < for (Number element : col) < if (item.equals(element)) < count++; >> > return count; >

A generic method can also use the wildcard . Kindly refer to this tutorial to recall the differences between extends and super wildcards. And remember that bounded types use only the extends keyword.

Читайте также:  Python django model values

Generally we can replace bounded types with wildcards in generic methods, but not in every case. Why? In the wildcard , ? is the type parameter to which cannot be referred in the method’s body; whereas in the bounded type , T is the type parameter to which can be referred in the method’s body. So if we want to use the type parameter in the method’s body, use bounded types.

References:

Other Java Collections Tutorial:

About the Author:

Nam Ha Minh is certified Java programmer (SCJP and SCWCD). He started programming with Java in the time of Java 1.4 and has been falling in love with Java since then. Make friend with him on Facebook and watch his Java videos you YouTube.

Add comment

Comments

Thanks for sharing your knowledge. Wouldn’t be better StudentDAO and ProfessorDAO implements DAO. DAO would have common methods. Would not that made for us easier creating factory pattern? After, we should use

Источник

Generic Methods

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter’s scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

The syntax for a generic method includes a list of type parameters, inside angle brackets, which appears before the method’s return type. For static generic methods, the type parameter section must appear before the method’s return type.

The Util class includes a generic method, compare, which compares two Pair objects:

public class Util < public static boolean compare(Pair p1, Pair p2) < return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue()); >> public class Pair  < private K key; private V value; public Pair(K key, V value) < this.key = key; this.value = value; >public void setKey(K key) < this.key = key; >public void setValue(V value) < this.value = value; >public K getKey() < return key; >public V getValue() < return value; >>

The complete syntax for invoking this method would be:

Pair p1 = new Pair<>(1, "apple"); Pair p2 = new Pair<>(2, "pear"); boolean same = Util. compare(p1, p2);

The type has been explicitly provided, as shown in bold. Generally, this can be left out and the compiler will infer the type that is needed:

Pair p1 = new Pair<>(1, "apple"); Pair p2 = new Pair<>(2, "pear"); boolean same = Util.compare(p1, p2);

This feature, known as type inference, allows you to invoke a generic method as an ordinary method, without specifying a type between angle brackets. This topic is further discussed in the following section, Type Inference.

Источник

Оцените статью