Hello, Jim.
So they are going to answer questions, what are generics in Java and how they work?
We'll start from understanding of why do we need generics.
After that, we'll understand how you can parametrized your mascots and classes.
You're going to understand what Boundy generics in Java are.
We'll discuss generics with multiple mounts also in this class.
And we are going to learn what the wild cards are and how to use them.
Today, we're going to have a lot of practice that will help you to understand generics.
And at the end of the lesson, I will explain what a type is and what type erasure is.
Let's start and to make you understand what generics are.
I suggested starting from examples to make you understand what problem generics intended to solve.
Imagine the next iteration.
You have array of strings and you want to have mastered that process is data.
For the sake of example, let's imagine that you need to have masses that would print all elements of
array to console in some custom way.
Here I created massive print array that takes array of strings as Masad argument inside a grade for
each loop and before printing any new element.
I also print words aliment and only after that some element is printed.
Now you realized that you have not only array of strings in your program, you also want to apply the
same behavior for an array of integers.
But the existing method will not work with an array of integers since it is specified that it works
only with the array of strings.
Let me uncommon this line and you can see a compilation error here.
What to do in this case, create another method that will work with array of integers, I assure.
What would you do in case you would need the same method for array of numbers or array of your custom
type user, for example?
That is definitely not the best option.
But what I would recommend to do in this case, it would be perfect to create the method that would
work with arrays of different types, what they think.
But how to do this in Java, that is exactly what generic mechanism is created for.
In other words, we can create parameterized methods and classes that would work with different types.
And here you already can see the first advantage of using generics you can avoid of code duplication
and increase cautery usage in your program.
Let me comment this parametrized method and explain use syntax.
This method is similar to the one we already have, but before the type of returns value, we have just
word here.
I write it in Diamond Operator.
These characters from both sides are called together as diamond operator whispered Sify, our parameterized
type in the diamond operator.
This is also called generic and will explain in simple words what this is about.
This is a placeholder for some specific type.
Theoretically, I can write Annunziata here.
The main thing is that letter that I specified here will be the same as in the Massett parameters and
Method Masmoudi.
And after I wrote it here, I can treat this E as a specific type and use it with parameters, for example,
or anywhere inside the method.
But here you can see that I have array of type E and while iterating over each element in this area,
I also use E type for my variable here.
Now I can call this method for most string and integer array the same as for all other types.
Let me comment, Prenter, a message that works only with strings and leave my generic method awesome.
And you can see that I don't have any compilation error anymore here.
Let's make a definition of generic method now.
Generic method is a method that introduce its own type parameters.
I believe now you understand what generics are and why do we need them.
Also, you know how to parametrized your methods now, but what is a potential issue in this case?
In this case, for the type inside the method body?
I can use only Massett that are available for object type, but not any other methods that are specific
to string or integer or any other type Y because compiler doesn't know what type will be here during
the runtime.
I can't call here any string methods because compiler can guarantee that only objects of type streambed
will be here.
As we can see on this example, objects of any type might be here and what to do in case I need some
behavior that is related to some specific hierarchy of objects.
What if I know that this method wouldn't work with any objects at all?
But this method has to work with any type that implements comparable interface.
In this case, I can count on the fact that objects inside my body will have compared to Masset and
I will be able to use this API in the mess and body.
Does it make sense?
To help you understand this, let me open another example here, I opened another file where I prepared
other examples for you.
Let's imagine that I have to create a that would be able to find the greatest value out of three variables.
And I want to implement generic method, but not a bunch of separate methods that works with only strings,
only integers, only double values.
How to do that generic with boundaries will help us with this task.
They're also called bounded type parameters.
Here you can see massive declaration.
The methods name is max value.
The next thing that you can notice here is that we use different letter here now instead of E, we have
key and he is parameterization of my method here in Diamond Operator.
I say that this method should work with some type that extends comparable and comparable in turn is
parameterized with this type.
In other words, some type T should use it extends comparable interface or implement comparable interface.
This is called upper bound with parameterization because we set boundary from the top.
Doesn't make sense.
Also, one more common here.
It is possible to make multiple sounds like it is specified here in command line.
I will leave it here, command it so that you could learn this when you will download the source code
that I am showing right now.
So you can say that we should extend both comparable interface and compare the interface.
That's just for the sake of example.
I believe you understand that instead of comparator, you can specify any other type here and only in
case our custom type T will implement both of these interfaces.
Only in this case we could work with this method and methods, arguments of type T, you even can see
that I have a compilation error because neither string or integer nor verbal implements both of these
interfaces simultaneously.
Let me erase this to proceed with our demo potentially that you can also use time that is specified
for parameterization as a returned type so you can get the type that was used as parameters here.
The main thing and all of this is that this type should be the same everywhere across the MassArt in
case the first argument is string.
It is not possible that other arguments wouldn't be of different type or return type would be different.
And now Compiler is aware that this type will be compatible with comparable interface.
That means that all variables that are passed as arguments to this method have compared to MassArt.
That's why all logic that is written in this method is rely on the fact that we can use this method
and there are no any compilation errors.
That's great.
And here you can see in my method that I can invoke max value method with different types of arguments.
No matter is as this integer double or string type, let me run this program from the console output.
We can conclude that our method works as expected.
Now you know how the Parameterize method was bound.
Hobbs's makes things clearer.
Let's go to the next example and this example.
I'm going to show you how you can parameterize your class here.
You can see Diamont operator right next to my class name.
That means I parametrized my class, for example.
I can declare property of type key and Masset parameters like in this set method also can be of my type,
e.g. I can initialize my field with the values that will be passed as massas argument here.
The method will return me a value of type that we used to parametrized my class.
And here's example.
I declare the variable of type generic.
Mazrui was a name integer in box and used Diament operator to parameterize this type by integer.
After that I created the object and used Diament operator after Anjelah version seven.
There is no need to duplicate parameterization type in Diament operator here, but you can if you wish.
For example, here I left integer type and then my second variable was a name string box that I parametrized
by string.
I left blank my second Diament operator.
In most cases type is specified only when you declare a variable.
And here you can see that my integer box can work only with integers and there is a compilation error
in case I would try to set any string.
Object is a similar story with my string box.
That is because these two variables are parametrized with different types and in the first case set
Masset except on the integers.
And in the second case, set method accepts only strings.
When I use get MassArt and receive integer and string type accordingly.
And here you can see that I print values to console and used special format specifiers for that some.
Numbers and string accordingly, let me run this program, you can see that everything works as expected
without any errors.
Let's create a definition of generic type.
Now, when you saw the example and generic type is a generic class or interface that is parameterized
over types.
And now let's continue our lesson.
Even if you would not use parameterization and generics for your own custom classes, you would use
generics a lot with classes from Java collections framework.
It is super useful because it wasn't possible to create collections for all possible types.
That's why all types from Java collections framework are parametrized and use generics.
Let me open source code of arriviste.
You can see that it is parameterized with each type and if I open at Masset, you can see that it adds
elements of each type.
And get maced in return elements of each type, does it make sense sometimes even parametrized with
two types, for example, on implementations specify separate type for key and value.
Let me open hash map or squad here.
You can see that I can specify type for key and a separate type for Lélia just to placeholders and input
MassArt.
You can see that I put key of type K and value of type we and type of return value is the same as type
of the value we, because according to this interface we receive the last value that is associated with
a given key.
But the details of the interfaces of our collections framework are covered in the separate lessons.
Now you know how to parameterize your class and you know how types from Java collections framework are
parametrized.
We have one more important and practical thing to talk about.
I want to talk with you about the wild cards with upper and lower bounds.
Let me open another demo file that I prepared before this lesson.
To make you understand this example, let me show you all classes that will take part in our demo.
I declared all classes in one file to keep everything grouped for the demo and to not switch tabs and
engines that we have.
Some parent class and child class extends parent and grandchild.
Class extends child.
Is it clear?
Cool.
Let's move on.
Now imagine that I have at least parametrized by type child and I have mastered that can work with any
list objects that are parametrized by time parent.
It is called process parent elements.
How do you think MacColl process span's elements MassArt and POS collection of child elements there?
Logically, it seems to be OK since each child class should have all methods that parent class has and
compilers should not be worried about this.
But if I ask you this question, you already suspected something, don't you?
You are right.
If you are suspicious, this wouldn't work.
You can see compilation error here.
Why?
Because list of child elements is not a list of parent elements.
That said, what what to do in this case.
Usually in this case, engineers use wild cards.
Let me show you the next method we have process elements, method, the text list of elements that is
parametrized by any type that extends Berent.
This question mark is called wildcard and can be treated as any type.
This is called upper bound at wild card.
In this case, I can pass list that contains elements of type child to this method.
And here you can see that there are no errors.
Does it make sense?
Now I want you to put all the attention to the next example.
When you use cards, that means you can get elements of type parent from this collection.
I believe that is clear that you will not be able to extract element of type child from this list because
there is no guarantee what type would be here instead of the question mark during the runtime.
It can be child, but it also can be parent type.
And that's why custom foreign object to child type would cause exception.
And most likely, parent doesn't have all methods and behavior that is described in the child type.
Is it clear?
OK, we understood that we can get elements of type parent in case we use upper bound at wild card.
But can we add elements to this content?
Unfortunately we can't.
And here the examples you can see that I can't add neither parent nor child nor grandchild objects I
cannot only know value.
You might be wondering why why I can't add object of parent type to collection of elements, parametrized
by parent type.
But let's look at this from another side.
This is a very smart mechanism created in Java to save engineers from potential errors.
The main issue is that because we don't know what type would be here during the runtime, that's why
interactions with such collections are limited.
Imagine that during the runtime I will pass list of elements of type child and I will add element of
time parent.
And after I modified this collection in this method, I will get the element and I would think that
all elements in this container are of child type and I will call some child specific method.
What will happen?
I will get error during the runtime because indeed I will get here object of parent time and this object
will have child specific method.
That is why Compiler wants to eliminate cases like this and just doesn't allow me to add any elements
in this method.
Does it make sense and leave now you understood how upper bound at wild cards work.
Now let's take a look at another case.
What to do in case I have to create.
That will be able to add new elements to the collection in this case, we have to use lower bound at
wild cards here you can see that I created two more lists.
One of them is parametrized by parent type and another one is parametrized by child type.
And you can see that I call process elements to Masset and parse the first and the second list object
there.
But I can't pass list of elements that are parametrized by grandchild type.
Let's investigate process elements to MassArt.
This method has lower bound and wild card.
You can see super keyword here.
What does that mean?
That means that this method can take as an argument any object of type liste parametrized by any type
that is parent or in other words, super type with respect to child type.
That's why I can't pass Barrentine or super type of child.
This could be any super type, including parent and even object is clear.
If yes, then let's proceed.
In this case I can get on the elements of type object and I believe you already understood why.
Because there is no guarantee what super type of child type will be here during the runtime.
But what types I can put into this collection, I can put any time that this child with respect to child
type here, you can see that I can add object of type child and grandchild.
And also I always can add now, for example, I can't add any super type of type child, including parent
type and object y if this sounds completely not logical, then consider the next case.
Imagine that we have passed here container full of objects of type child and in case compiler let me
add the parent object.
And after this collection was modified, I retrieve object of child type from here and called child
specific method and program fails because that object doesn't have child specific behavior and case
I passed here at least parametrized by any other type that is super with respect to child type.
There is no harm in this at all to add child or grandchild object in this container because each of
the specific types for sure has behavior of its parent type.
Does it make sense?
We can make a conclusion in case you want to create a method that will work with collections, parametrized
by different types, and you want to use this method to modify the collection and to add elements there
you have to use lower bound at wild cards.
But in case you wanted to use this method only to read elements from the container to process that data,
then you have to use upper bound at wild cards because upper bound wild cards allow us to read from
the container specific type and use its methods to get all necessary data.
You already learned how to use bounded wild cards, but should we use wild cards without bounds?
I would say that this is super rare case.
That's why.
Let me show you this on example.
Let me open source code of absurd collection.
You can see that absolute collection is parametrized by any type.
But let's look at contains all.
Macit, can you see wild card here.
That means that contains some method can work with any collection that contains any types inside.
Then what is the difference between each type and wild card?
The point is that each type will be erased and substituted with a specific type everywhere with the
type that we will specify, whereas wild cards would always mean any type, but not the exactly one
type that we used to parameterize instance of our object.
Does it make things clearer?
I will leave linked to this example in attachments to this Larssen so that you could investigate it.
But pay attention that this specific example you won't be able to run because I described various scenarios
that causes compilation error.
But still you can play around with compilation properties to understand wildcards together with upper
and lower bounds.
Let me open the next demo file to show you the next example and this example.
I want you to understand the generic exist only for our compiler.
There are no generics during the runtime because of the type erasure type.
Erasure can be explained as a process of enforcing type constraints only at compile time and discarding
the element type information at runtime.
That means that during the runtime it can be that objects of another type will appear in collection.
Let me show this example and give my comments as we go and create at least Werrimull and list is parametrized
by integer type.
Everything looks good so far, but now I created a list without specifying any generic type.
That means that during the runtime, the EIB that is declared in the source code of list will be erased
and.
Aware where it was used, it will be substituted with object type, for example, at Mass, it will
work with any object.
But in case I use bouncin generics, like, for example, in our damos was a generic for our method
during the compilation.
This type will be erased and substituted with the first bound class, in this case comparable.
Does it make sense?
You can see that our compiler tells us that there is a one here.
It is not an error, but still works of our attention in the opinion of compiler.
If I am a mouse over this warning, I see explanation.
Message that tells me is at least is a type.
What is a raw type.
A raw type is the name of a generic class or interface.
Without any type arguments.
We can still work with such types.
But Compiler wants to warn us that something bad may happen.
Now I Telx at least reference is pointing out to the same object as integers reference.
And I put string object in that list.
And again, I see he has a warning about time safety, but not an error.
This is very unlikely that you will be able to reproduce this mistake.
But I want to prove you that generics exist only to help us during the compilation time, but not during
the runtime.
And as you can see, I successfully edit my string object to the list, but when in other parts of the
program or somewhere else will use the reference of our parametrized type integers and when will call
that method, we will get object of type integer because this variable has parameterized type.
But what would happen during the runtime?
What do we think?
Lets me around the program and show you this.
And we see runtime exception, glossiest exception.
Java tells us that in line 15 it can't costarring object to integer.
I believe you got the trick.
That's it.
Now you know about even more than some senior software engineers, believe me.
Let's recap what we have learned today.
Today, we learned a lot of different things.
Now you know what generics are and what potential problems they help us to solve.
Youso how to parameterize your method in Java, we learned what Boundy generics are together.
We created parameterized Closs.
You saw what wild cards are.
And we practice a bit with upper bound at wild cards and lower bound with wild cards.
And at the end of the lesson, we talked about hyperalgesia during the runtime.
And now let's look at your homework.
I believe you already implemented our custom list from the other homework I left link to that homework
here just in case.
Also, I count on the fact that you implemented Iterator and listed the return for this container.
Here is a link to the homework about iterators.
Now, I want you to make my list Interface Parametrized, and I want you to use generics for the fourth
E class implementation myside that I want you to make here in your classes that implements Iterator
and least iterator interfaces.
Also parametrized.
This will be a great practice for you to work with generics that it home.
This task will be like a piece of cake for you.
That's all what I have for you for today.
Thanks a lot for your attention.
See you in the next lesson.