JavaRush /Java Blog /Random EN /What are generics in Java

What are generics in Java

Published in the Random EN group
Hello! Today we will talk about generics. I must say that you will learn a lot of new things! Not only this, but also the next few lectures will be devoted to generics. What are generics in Java - 1 Therefore, if this topic is interesting to you, you are lucky: today you will learn a lot about the features of generics. Well, if not, calm down and relax! :) This is a very important topic and you need to know it. Let's start with a simple one: “what” and “why”. What are generics? Generics are types with a parameter. When creating a generic, you specify not only its type, but also the type of data it should work with. I think the most obvious example has already come to your mind - this is ArrayList! Here's how we usually create it in the program:
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
As you might guess, the peculiarity of the list is that it will not be possible to “stuff” everything into it: it works exclusively with objects String. Now let's take a short excursion into the history of Java and try to answer the question: “why?” To do this, we ourselves will write a simplified version of the ArrayList class. Our list can only add data to the internal array and receive this data:
public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
Let's say we want our list to store only numbers Integer. We don't have generics. We cannot explicitly specify the o instance of check Integerin the add(). Then our entire class will be suitable only for Integer, and we will have to write the same class for all data types existing in the world! We decide to rely on our programmers and simply leave a comment in the code so that they do not add anything unnecessary there:
//use it ONLY with Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
One of the programmers missed this comment and inadvertently tried to put numbers mixed with strings in the list, and then calculate their sum:
public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
Console output: 300 Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at Main.main(Main.java:14) What's the worst in this situation? Far from being a programmer's inattentiveness. The worst thing is that the wrong code ended up in an important place in our program and compiled successfully . Now we will see the error not at the coding stage, but only at the testing stage (and this is in the best case!). Fixing bugs later in development costs a lot more - both money and time. This is precisely the advantage of generics: a generic class will allow an unlucky programmer to detect an error immediately. The code simply won't compile!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();

       myList1.add(100);
       myList1.add(100);
       myList1.add("Lolkek");//error!
       myList1.add("Shalala");//error!
   }
}
The programmer will immediately “come to his senses” and instantly correct himself. By the way, we didn't have to create our own class Listto see this kind of error. Simply remove the type brackets ( <Integer>) from a regular ArrayList!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
Console output: 300 Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at Main.main(Main.java:16) That is, even using “native” tools Java, you can make this mistake and create an unsafe collection. However, if we paste this code into IDEa, we see a warning: “ Unchecked call to add(E) as a member of raw type of java.util.List ” This tells us that something might go wrong when adding an element to a collection without generics not this way. But what does the phrase “raw type” mean? The literal translation will be quite accurate - “ raw type ” or “ dirty type ”. Raw typeis a generic class from which its type has been removed. In other words, List myList1this is Raw type. The opposite raw typeis generic typea generic class (also known as a class parameterized type), created correctly, with a type specification. For example, List<String> myList1. You might have a question: why is it even allowed to use raw types? The reason is simple. The creators of Java left support in the language raw typesso as not to create compatibility problems. By the time Java 5.0 was released (generics appeared for the first time in this version), a lot of code had already been written using raw types. Therefore, this possibility still exists today. We have already mentioned Joshua Bloch's classic book “Effective Java” more than once in lectures. As one of the creators of the language, he did not ignore the topic of using raw typesand in the book generic types. What are generics in Java - 2Chapter 23 of this book has a very eloquent title: “Don't use raw types in new code.” This is something you need to remember. When using generic classes, never turn them generic typeinto raw type.

Typed Methods

Java allows you to type individual methods, creating so-called generic methods. Why are such methods convenient? First of all, because they allow you to work with different types of parameters. If the same logic can be safely applied to different types, a generic method is a great solution. Let's look at an example. Let's say we have some kind of list myList1. We want to remove all values ​​from it, and fill all the free spaces with a new value. This is what our class with a generic method will look like:
public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Старая строка 1");
       strings.add("Старая строка 2");
       strings.add("Старая строка 3");

       fill(strings, "Новая строка");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
Pay attention to the syntax, it looks a little unusual:
public static <T> void fill(List<T> list, T val)
The return type is preceded by <T>, which indicates a generic method. In this case, the method takes 2 parameters as input: a list of objects T and another separate object T. By using <T>, the typing of the method is achieved: we cannot pass a list of strings and a number there. A list of strings and a string, a list of numbers and a number, a list of our objects Catand another object Cat- that’s the only way. The method main()clearly demonstrates that the method fill()easily works with different types of data. First, it takes as input a list of strings and a string, and then a list of numbers and a number. Console output: [Newline, Newline, Newline] [888, 888, 888] Imagine if fill()we needed method logic for 30 different classes, and we didn’t have generic methods. We would be forced to write the same method 30 times, just for different data types! But thanks to generic methods, we can reuse our code! :)

Typed classes

You can not only use the generic classes provided in Java, but also create your own! Here's a simple example:
public class Box<T> {

   private T t;

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

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Старая строка");
       System.out.println(stringBox.get());
       stringBox.set("Новая строка");

       System.out.println(stringBox.get());

       stringBox.set(12345);//ошибка компиляции!
   }
}
Our class Box<T>(“box”) is typed. Having assigned a data type ( ) to it during creation <T>, we will no longer be able to place objects of other types into it. This can be seen in the example. When creating, we specified that our object will work with strings:
Box<String> stringBox = new Box<>();
And when in the last line of code we try to put the number 12345 inside the box, we get a compilation error! Just like that, we created our own generic class! :) This concludes our lecture today. But we are not saying goodbye to generics! In the next lectures we'll talk about more advanced features, so don't say goodbye! ) Good luck in your studies! :)
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION