-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Avinandan Bose edited this page Mar 26, 2023
·
1 revision
This is all about Java Generics
Java includes support for writing generic classes and methods that can operate on variety of data types while often avoiding the need for explicit casts. The generics framework allow us to define a class in terms of a set of formal type parameters, which can then be used as the declared type for variables , parameters, and return values within the class definition. Those formal type parameters are later specified when using the generic class as a type elsewhere in a program. Generics are a feature of Java that allows us to create classes and methods that work with different types of data.Generics are a way to make our code more flexible and reusable.
Diamond Operator: In Java Generics introduced " < > " Diamond Operator which is a Java 7 feature , to simplify the use of generics when creating an object. Along with it , introduced Type Erasure technique, as discussed below.
Note: As language Java is based on OOP based , java.lang.Object is the root of the class hierarchy, and we can create any object of Object class and every class we create or predefined gets instantiated under Object class , hence Object class is Super class of every class.
Similarly Byte(java.lang.Byte), String (java.lang.String), Integer(java.lang.Integer) , Float(java.lang.Float), Double(java.lang.Short) etc. are wrapper classes comes from Java's Lang package as they not only wraps , put a cast over a value and change its type but also perform some specific functions - gives a pure OOP concept .
On the above example , String , Integer , Double are such wrapper classes that puts a narrow explicit cast : Object to String/Integer/Double . The above example is the Representation a generic pair using a classic style in which code become used to with such explicit casts .
illegal; Compiler Error
-------------------------
String stock = p1.getFirst();
String stock2 = p2.getFirst();
Double price = p1.getSecond();
Integer price2 = p2.getSecond();
Hence using Java Generic Framework we can remove such explicit casts. In the framework we have to use a pair class using formal type of parameters to represent the two relevant types in our composition. An implementation using this framework is given in the Code below:
class Pair <A,B> {} : Here A and B are formal type parameters.
Pair<Integer, String> p1 : Here Integer , String are Actual Parameters.
So, we can write :
p1 = new Pair<Integer, String>(1, "apple");
1. Type 1 Representation of Java Generic Framework:
Pair<Integer, String> p1 ;
p1 = new Pair<Integer, String>(1, "apple"); //Generic Type Parameters are explicitly specified between Angle Bracket.
2. Also, We Can subsequestly instantiate the generic class using the following syntax:
Pair<Integer, String> p3 ;
p3 = new Pair<>(3, "orange"); //Rely on type interference.
3. Classic type of Instantiation of Object:
Pair<Integer, String> p2 ;
p2 = new Pair(2, "pear"); // Classic Type
After the new operator , we provide the name of the generic class , then an empty set of angle brackets(known as "diamond") and finally the parameters to the constructor. An instance of the generic class is created , with the actual types for the formal type parameters determined based upon the original declaration of the variable to which it is assigned . This process is known as Type Inference and was introduced to the generics framework in JAVA SE 7 .
The above style existed prior to JAVA SE 7 , in which the generic type parameters are explicitly specified between angle brackets during instantiation.
However , it is important that one of the above styles used . If angle brackets are entirely omitted as shown above, this reverts to the classic style , with Object automatically used for all generic type parameters and resulting in a compiler warning to a variable with more specific types.
int price = p1.getFirst();
double price2 = p2.getSecond();
Here automatic unboxing occurs , where Integer and Double wrapper type , convert to their primitive value of int , double primitive data type.
- Generics and Arrays[Using For Loop]- Eg-1
- Generics and Arrays[Using For Loop]- Eg-2
- Generics and Arrays[Using For Loop]- Eg-3
- Generics and Arrays[Using For Loop]- Eg-4
java_generics5<Integer, String>[] holdings;
:Array of Object:
holdings = new java_generics5[2];
Size of the Array of Object holdings → 2
:-------------------------------------------:
holdings[0] = new java_generics5<>(1, "apple");
Hence:
holdings[0].getFirst() → Returns Integer (1)
holdings[0].getSecond() → Returns String ("apple")
:-------------------------------------------:
holdings[1] = new java_generics5<>(2, "pear");
Hence:
holdings[1].getFirst() → Returns Integer (2)
holdings[1].getSecond() → Returns String ("pear")
java_generics5<Integer, String>[] holdings;
:Array of Object:
holdings = new java_generics5[2];
Size of the Array of Object holdings → 2
:-------------------------------------------:
holdings[0] = new java_generics5<>(1, "apple");
Hence:
holdings[0].getFirst() → Returns Integer (1)
holdings[0].getSecond() → Returns String ("apple")
:-------------------------------------------:
holdings[1] = new java_generics5<>(2, "pear");
Hence:
holdings[1].getFirst() → Returns Integer (2)
holdings[1].getSecond() → Returns String ("pear")
:Array of Object:
T[] data; → Also A Member Instance of Class
:Creating Object of A Generic Class:
java_generics6<Integer> p1;
p1 = new java_generics6<Integer>()
:It represents :
Integer[] data ; → The formal Argument replaced by actual i.e. Integer.
And p1 is object of the Generic Class.
:Allocation of Size of the Array of Object:
p1.data = new Integer[2];
:Elements Assigned:
p1.data[0] = 1;
p1.data[1] = 2;
:Fetching Elements from Array:
for (Integer i =0 ; i < p1.data.length; i++) {
System.out.println(p1.data[i]);
}
:Array of Object:
T[] data; → Also A Member Instance of Class
:Creating Object of A Generic Class:
java_generics6<Integer> p1;
p1 = new java_generics6<Integer>()
:It represents :
Integer[] data ; → The formal Argument replaced by actual i.e. Integer.
And p1 is object of the Generic Class.
:Allocation of Size of the Array of Object:
p1.data = new Integer[2];
:Elements Assigned:
p1.data[0] = 1;
p1.data[1] = 2;
:Fetching Elements from Array:
for (Integer i =0 ; i < p1.data.length; i++) {
System.out.println(p1.data[i]);
}
class A2<T>{
T data;
A2(T item){
data = item;
}
int view(){
return(Integer)data;
}
public static void main(String[] args) {
A2<Integer> p1;
p1 = new A2<>( 2);
System.out.println(p1.view());
}
}
class A2<T>{
T data;
A2(T item){
data = item;
}
T view(){
return data;
}
public static void main(String[] args) {
A2<Integer> p1;
p1 = new A2<>( 2);
System.out.println(p1.view());
}
}
class A2<S,T>{
T data1;
S data2;
A2(T item1, S item2){
data1 = item1;
data2 = item2;
}
T view1(){
return data1;
}
S view2(){
return data2;
}
public static void main(String[] args) {
A2<Integer, Integer> p1;
p1 = new A2<>( 10, 2);
System.out.println(p1.view1());
System.out.println(p1.view2());
}
}
class A2<S,T>{
T data1;
S data2;
A2(T item1, S item2){
data1 = item1;
data2 = item2;
}
T view1(){
return data1;
}
S view2(){
return data2;
}
public static void main(String[] args) {
A2<Integer, String> p1;
p1 = new A2<>( "Hello",2);
System.out.println(p1.view1());
System.out.println(p1.view2());
}
}
class A2{
public static <A> void view(A item) {
System.out.println("The value is: " + item);
}
public static <B> String view2(B item) {
return (String) item;
}
public static void main(String[] args) {
view(2);
System.out.println(view2("Hello"));
}
}
class A2<T>{
<A> T view(T data, A item) {
System.out.print(item);
return data;
}
public static void main(String[] args) {
A2<Integer> a2 = new A2<>();
System.out.println(a2.view(12, "Hello"));
}
}
class A2<T>{
<T> T view(T data, T item) {
System.out.print(item);
return data;
}
}
Here T represents the Type T of Generic Method but not of Parameterized Class.
Hence:
class A2<T>{
<T> T view(T data, T item) {
System.out.print(item);
return data;
}
public static void main(String[] args) {
A2<Integer> a2 = new A2<>();
System.out.println(a2.view("A", "Hello"));
}
}
class A2<T>{
<A> T view(T data, A item) {
return item;
}
:Will generate error as return type now needs generic class's T type as:
:T view() i.e. return Generric class T type,:
:though Generic method have an extended type A which has scope to the Method only.:
- Generic Methods AND Generic Arrays(Print Data Element) -Eg-18
- Generic Methods AND Generic Arrays(Swapping)- Eg-19
- Generic Methods AND Generic Arrays(Print Data Element 2)- Eg-20
- Generic Methods AND Generic Arrays(Add Elements In An Array)- Eg-21
- Generic Methods AND Generic Arrays(Reverse The Elements In An Array)- Eg-22
- Generic Methods AND Generic Arrays- Eg-23
- 1. Type 1 [Using Class Generics]
Note:
: As the Class is Generic :
:b ) Overloading by Difference in Sequence or Order of Parameter (Is Not Possible ):
Eg:
class A<T,K>{
void swap(T a, K b ){
}
void swap(K b, T b ){
}
//Is Not Possible
:c ) Overloading by Difference in datatypes of parameters passed in methods (Is Not Possible ):
Eg:
class A<T,K>{
void swap(T a, T b ){
}
void swap(K a, K b ){
}
//Is Not Possible
Generics classes can be part of a class hierarchy in just the same way as a non-generic class. Thus a generic class can act as a super-class or be a subclass. The key difference between generic and non-generic is that in a generic hierarchy, any type arguments needed by a generic super-class must be passed up the hierarchy by all subclasses. This is similar to the way that constructor arguments must be passed up a hierarchy.
class Gen<T> {
T obj;
Gen(T o) {
obj = o;
}
T getObj() {
return obj;
}
}
public class GenHierarchy1 <T> extends Gen<T> {
GenHierarchy1(T o) {
super(o);
}
public static void main(String args[]) {
GenHierarchy1<String> x = new GenHierarchy1<String>("Generics Test");
System.out.println(x.getObj());
}
class Gen<T> {
T obj;
Gen(T o) {
obj = o;
}
T getObj() {
return obj;
}
}
public class GenHierarchy1 <T> extends Gen<T> {
GenHierarchy1(T o) {
super(o);
}
public static void main(String args[]) {
GenHierarchy1<String> x = new GenHierarchy1<String>("Generics Test");
System.out.println(x.getObj());
}
It passes String as type parameter to Gen. Thus the obj inside the Gen portion of Gen2 will be of type Integer.
class Gen<T> {
T obj;
Gen(T o) {
obj = o;
}
T getObj() {
return obj;
}
}
public class GenHierarchy2 extends Gen<String> {
<T> GenHierarchy2(T o) {
super((String)o);
}
public static void main(String args[]) {
GenHierarchy2 x = new GenHierarchy2("Generics Test");
System.out.println(x.getObj());
}
}
class Gen<T> {
T obj;
Gen(T o) {
obj = o;
}
T getObj() {
return obj;
}
}
public class GenHierarchy2 extends Gen<String> {
<T> GenHierarchy2(T o) {
super((String)o);
}
public static void main(String args[]) {
GenHierarchy2 x = new GenHierarchy2("Generics Test");
System.out.println(x.getObj());
}
}
Notice also that GenHierarchy2 does not use the type parameter T except to support the Gen superclass. Thus, even if a subclass of a generic superclass would otherwise not need to be generic, it still must specify the type parameter(s) required by its generic superclass. If type "T" is not specified then we have to specify the type of class Gen whether Integer, String etc. during Inheritance and then compiler will ask to have a constructor of Sub Class as super Generic class have a constructor and the SubClass's constructor will have a cast of Type "< T >" as shown above.
class Gen<T> {
T obj;
Gen(T o) {
obj = o;
}
T getObj() {
return obj;
}
}
public class GenHierarchy3<V> extends Gen<V> {
GenHierarchy3(V o) {
super(o);
}
public static void main(String args[]) {
GenHierarchy3<String> x = new GenHierarchy3<String>("Generics Test");
System.out.println(x.getObj());
}
}
class Gen<T> {
T obj;
Gen(T o) {
obj = o;
}
T getObj() {
return obj;
}
}
public class GenHierarchy3<V> extends Gen<V> {
GenHierarchy3(V o) {
super(o);
}
public static void main(String args[]) {
GenHierarchy3<String> x = new GenHierarchy3<String>("Generics Test");
System.out.println(x.getObj());
}
}
Here if Sub Class have single Type parameter and if it changes , then the same type must be put to the "Type" to the Parent class.
class Gen<T> {
T obj;
Gen(T o) {
obj = o;
}
T getObj() {
return obj;
}
}
public class GenHierarchy4<V,T> extends Gen<T> {
V ob2;
GenHierarchy4(T o, V o2) {
super(o);
ob2 = o2;
}
V getOb2() {
return ob2;
}
public static void main(String args[]) {
GenHierarchy4<Integer, String> x =
new GenHierarchy4<>("Generics Test", 88);
System.out.println(x.getObj());
System.out.println(x.getOb2());
}
}
class Gen<T> {
T obj;
Gen(T o) {
obj = o;
}
T getObj() {
return obj;
}
}
public class GenHierarchy4<V,T> extends Gen<T> {
V ob2;
GenHierarchy4(T o, V o2) {
super(o);
ob2 = o2;
}
V getOb2() {
return ob2;
}
public static void main(String args[]) {
GenHierarchy4<Integer, String> x =
new GenHierarchy4<>("Generics Test", 88);
System.out.println(x.getObj());
System.out.println(x.getOb2());
}
}
class Gen3<T>{ ... }
class Gen4<T> extends Gen3<T>{ ... }
class HierDemo{
Gen3<Integer> iOb = new Gen3<Integer>();
Gen4<Integer> iOb2 = new Gen4<Integer>();
Gen4<String> strOb2 = new Gen4<String>();
if(iOb2 instanceof Gen4<Integer>){...}
if (iOb2 instanceof Gen3<Integer>) {...}
if(strOb2 instanceof Gen4<String>){...}
if (strOb2 instanceof Gen3<String>) {...}
if (iOb instanceof Gen3<Integer>) {...}
if (iOb instanceof Gen4<Integer>) {...}
}
class Gen3<T>{ ... }
class Gen4<T> extends Gen3<T>{ ... }
class HierDemo{
Gen3<Integer> iOb = new Gen3<Integer>();
Gen4<Integer> iOb2 = new Gen4<Integer>();
Gen4<String> strOb2 = new Gen4<String>();
if(iOb2 instanceof Gen4<Integer>){...}
if (iOb2 instanceof Gen3<Integer>) {...}
if(strOb2 instanceof Gen4<String>){...}
if (strOb2 instanceof Gen3<String>) {...}
if (iOb instanceof Gen3<Integer>) {...}
if (iOb instanceof Gen4<Integer>) {...}
}
We can cast one instance of a generic class into another only if the two are otherwise compatible and their type arguments are the same. For example, assuming the foregoing program, this cast is legal:
((Gen<Integer>) iOb2 ).obj = 100; //Legal
((Gen<String>) strOb2).obj = "Generics Test 2";// Legal
((Gen<Integer>) iOb).obj = 1000;// Legal
((Gen<Integer>) iOb2 ).obj = 100; //Legal
((Gen<String>) strOb2).obj = "Generics Test 2";// Legal
((Gen<Integer>) iOb).obj = 1000;// Legal
class Gen9<T> {
T ob;
Gen9(T o) {
ob = o;
}
T getob() {
System.out.print("Gen9's getob(): ");
return ob;
}
}
class Gen10<T> extends Gen9<T> {
Gen10(T o) {
super(o);
}
// Override getob()
T getob() {
System.out.print("Gen10's getob(): ");
return ob;
}
}
class OverrideDemo {
public static void main(String args[]) {
Gen9<Integer> iOb = new Gen9<Integer>(88);
Gen10<Integer> iOb2 = new Gen10<Integer>(99);
Gen10<String> strOb2 = new Gen10<String>("Generics Test");
System.out.println(iOb.getob());
System.out.println(iOb2.getob());
System.out.println(strOb2.getob());
}
}
Output
-----------
Gen9's getob(): 88
Gen10's getob(): 99
Gen10's getob(): Generics Test
class Gen9<T> {
T ob;
Gen9(T o) {
ob = o;
}
T getob() {
System.out.print("Gen9's getob(): ");
return ob;
}
}
class Gen10<T> extends Gen9<T> {
Gen10(T o) {
super(o);
}
// Override getob()
T getob() {
System.out.print("Gen10's getob(): ");
return ob;
}
}
class OverrideDemo {
public static void main(String args[]) {
Gen9<Integer> iOb = new Gen9<Integer>(88);
Gen10<Integer> iOb2 = new Gen10<Integer>(99);
Gen10<String> strOb2 = new Gen10<String>("Generics Test");
System.out.println(iOb.getob());
System.out.println(iOb2.getob());
System.out.println(strOb2.getob());
}
}
Output
-----------
Gen9's getob(): 88
Gen10's getob(): 99
Gen10's getob(): Generics Test
Here Inference : means Compiler decision making ability to decide where to put the Context based on its Type. Hence its known as Type Inference . That is :
class MyClass<T, V> {
T ob1;
V ob2;
MyClass(T o1, V o2) {
ob1 = o1;
ob2 = o2;
}
public static void main(String[] args) {
MyClass<Integer, String> a = new MyClass<Integer, String>(100, "Generics");
}
}
class MyClass<T, V> {
T ob1;
V ob2;
MyClass(T o1, V o2) {
ob1 = o1;
ob2 = o2;
}
public static void main(String[] args) {
MyClass<Integer, String> a = new MyClass<Integer, String>(100, "Generics");
}
}
Here MyClass < Integer , String > a = new MyClass < Integer , String > ( 100 , " Generics " ) ; here ,Compiler decides that 100 is for Integer and " Generics " for String . As we know " < > " is known as Diamond Operator and Syntax it goes like:
class-name<type-arg-list> var-name =
new class-name<>(cons-arg-list);
Where,
cons→Constructor.
arg→Argument.
class-name<type-arg-list> var-name =
new class-name<>(cons-arg-list);
Where,
cons→Constructor.
arg→Argument.
boolean isEqual(MyClass<T, V> o) {
if (ob1 == o.ob1 && ob2 == o.ob2) {
return true;
} else {
return false;
}
}
MyClass<Integer, String> a = new MyClass<Integer, String>(100, "Generics");
if (a.isEqual(new MyClass<Integer, String>(100, "Generics"))) {
System.out.println("Equal");
} else {
System.out.println("Not Equal");
}
boolean isEqual(MyClass<T, V> o) {
if (ob1 == o.ob1 && ob2 == o.ob2) {
return true;
} else {
return false;
}
}
MyClass<Integer, String> a = new MyClass<Integer, String>(100, "Generics");
if (a.isEqual(new MyClass<Integer, String>(100, "Generics"))) {
System.out.println("Equal");
} else {
System.out.println("Not Equal");
}
- 1. There are times when we want to restrict the kind of types that are allowed to be passed to a type parameter.
- 2. To declare a bounded type parameter, list the parameter's name , followed by the extends keyword.
- 3. Parameter followed by extends called as upper bound.
- Upper Bound Eg-1
- Upper Bound Eg-2
- Upper Bound Eg-3[String]
- Upper Bound Eg-4[Byte]
- Upper Bound Eg-5[StringBuffer]
- Upper Bound Eg-6[StringBuilder]
- Upper Bound Eg-7[Integer]
- Upper Bound Eg-8[Float]
- Upper Bound Eg-9[Character]
- Upper Bound Eg-10[Short]
- Upper Bound Eg-11[Long]
- Upper Bound Eg-12[Comparable]
- Upper Bound Eg-13[Serializable]
- Upper Bound Eg-14[Runnable]
- Upper Bound Eg-15[Thread]
- Upper Bound Eg-16[List]
- Upper Bound Eg-17[Map]
- Upper Bound Eg-18[CharSequence]
- Upper Bound Eg-19[Object]
- Upper Bound Eg-20[Set]
- 2. T is a type class can extends a Class and an Interface joined through '&' AND.
- 3. Note the class being extended by type T must implement the Interface .
- 4. No 'implement' Keyword is allowed it will generate error.
Note: The abstract class Number is the superclass of platform classes representing numeric values that are convertible to the primitive types byte, double, float, int, long, and short.
Note: The type T here shouldnot be bounded by final type of String,Byte, StringBuffer, StringBuilder,Integer,Float,Character,Short and Long. Its noted that all belongs to package: java.lang .And all of these are wrapper classes(except StringBuilder and StringBuffer as they are companion classes of String and they are mutable classes unlike String) declared as "Final" . And the rule is we cannot inherit them as they are final classes . Here T extends String,Byte,... etc. represents the type T is String,Byte,... etc. Final types cannot be further extended. Hence generates a warning and equally Legal representation.Hence types are fixed here i.e. if T extends String , Then during object creation the type must be String not Integer, Float and other types and it is same for others as given in above examples.
interface A{
}
class B imlpements A{
}
class Ex<T extends B & A>{
Ex<B> ex = new Ex<>();
}
:Here B is Class:
:Here A is Interface:
:And Class B implements Interface A:
:And Type for Generic Class Ex is the Class which implements the Interface:
:i.e. Ex<B> ex = new Ex<>():
interface A {}
interface B {}
Class C implements A , B{}
class Ex<T extends A & B>{
Ex<C> ex = new Ex<>();
}
:Here B is Interface:
:Here A is Interface:
:And Class C implements Interface A and Interface B:
:And Type for Generic Class Ex is the Class which implements the Interface:
:i.e. Ex<C> ex = new Ex<>():
- Now for Abstract Methods the Class which extends Abstract Class and implement Interface becomes type T of Generic Class.But there is a difference that is we cannot use Generic type to extends the Class which extends Abstract Class if Abstract Class is extended by Generic Type 'T'. That we can only have one class and 'n' number of interfaces.
interface A{}
interface B{}
abstract class C{}
class D extends extends C implements A,B{}
class Ex<T extends C & A & B>{
Ex<D>ex = new Ex<>();
}
:OR:
interface A{}
interface B{}
abstract class C{}
class D extends extends C implements A,B{}
class Ex<T extends D & A & B>{
Ex<D>ex = new Ex<>();
}
:i.e. T either extends D or abstract Class C:
java.lang.Number → Number is abstract class of java.lang package.
java.lang.Comparable<T> → Comparable<T> is interface of java.lang package.
java.lang.Number → Number is abstract class of java.lang package.
java.io.Serializable → Serializable is interface of java.io package.
java.lang.Thread → Thread is a class of java.lang package ,
which implements interface Runnabe.
java.lang.Runnable → Runnable is an interface of java.lang package ,
implemented by Thread class.
java.util.HashMap → HashMap is a Class which is in java.util package.
Hash Map implements Map interface.
java.util.Map → Map is an interface of java.util package ,
implemented by HashMap class.
java.util.ArrayList → ArrayList is a Class which is in java.util package.
ArrayList implements List interface.
java.util.List → List is an interface of java.util package ,
implemented by ArrayList class.
java.util.HashSet → HashSet is a Class which is in java.util package.
HashSet implements Set interface.
java.util.Set → Set is an interface of java.util package ,
implemented by HashSet class.
java.lang.CharSequence → CharSequence is an interface in java.lang package.
java.lang.Comparable<T> → Comparable is an interface in java.lang package.
java.lang.Object → Object is an interface in java.lang package.
Class Object is the root of the class hierarchy.
Every class has Object as a superclass.
java.lang.Comparable<T> → Comparable is an interface in java.lang package.
Syntax:
public static <Type extends Class/Interface> returnType funcName(parameter){
//code
}
Syntax:
<Type extends Class/Interface> returnType funcName(parameter){
//code
}
public static void main(String[] args)
{
class_name<Type> var/obj_name = class_name<>();
:Or if not generic class:
class_name var/obj_name = class_name();
}
Note: Its better to continue with static method rather than of creation of object.
Other Examples- Static Upper Bound Generic Methods
- Upper Bound Generic Type Methods →Eg-13 [T extends Serializable]
- Upper Bound Generic Type Methods →Eg-14 [T extends Runnable]
- Upper Bound Generic Type Methods →Eg-15 [T extends Thread]
- Upper Bound Generic Type Methods →Eg-16 [T extends List]
- Upper Bound Generic Type Methods →Eg-17 [T extends Map]
- Upper Bound Generic Type Methods →Eg-18 [T extends CharSequence]
- Upper Bound Generic Type Methods →Eg-19 [T extends Object]
- Upper Bound Generic Type Methods →Eg-20 [T extends Set]
- Multiple Upper Bound Generic Type Methods →Eg-1 [T extends Number & Comparable]
- Multiple Upper Bound Generic Type Methods →Eg-2 [T extends Number & Serializable]
- Multiple Upper Bound Generic Type Methods →Eg-3 [T extends Thread & Runnable]
- Multiple Upper Bound Generic Type Methods →Eg-4 [T extends HashMap & Map]
- Multiple Upper Bound Generic Type Methods →Eg-5 [T extends ArrayList & List]
- Multiple Upper Bound Generic Type Methods →Eg-6 [T extends HashSet & Set]
- Multiple Upper Bound Generic Type Methods →Eg-7[T extends CharSequence & Comparable]
- Multiple Upper Bound Generic Type Methods →Eg-8[T extends Object & Comparable]
- Multiple Upper Bound Generic Type Methods →Eg-9 [T extends Number & Comparable]
- Multiple Upper Bound Generic Type Methods →Eg-10 [T extends Number & Serializable]
- Multiple Upper Bound Generic Type Methods →Eg-11 [T extends Thread & Runnable]
- Multiple Upper Bound Generic Type Methods →Eg-12 [T extends HashMap & Map]
- Multiple Upper Bound Generic Type Methods →Eg-13 [T extends ArrayList & List]
- Multiple Upper Bound Generic Type Methods →Eg-14 [T extends HashSet & Set]
- Multiple Upper Bound Generic Type Methods →Eg-15[T extends CharSequence & Comparable]
- Multiple Upper Bound Generic Type Methods →Eg-16[T extends Object & Comparable]
- 1. In generic code, the question mark (?), called the wildcard, represents an unknown type.
- 2. The wildcard is never used as a type argument for a generic method invocation, a generic class instance creation, or a supertype.
- 3.The wildcard can be used in a variety of situations such as the type of a parameter, field, or local variable; sometimes as a return type.
- 4.Unlike arrays, different instantiations of a generic type are not compatible with each other, not even explicitly. This incompatibility may be softened by the wildcard if ? is used as an actual type parameter.
graph TD;
Wildcards-->| | UpperBoundedWildcards;
Wildcards-->| | LowerBoundedWildcards;
Wildcards-->| | UnBoundedWildcards;
- To declare a upper bounded Wildcard parameter, list the ?, followed by the extends keyword, followed by its upper bound.
- Upper Bounded Wildcards →Eg-1
interface A {
}
class Example<T> implements A {
}
public class WildCards<T extends Example<? extends A>>{
public static void main(String[] args) {
WildCards<Example<A>> obj = new WildCards<>();
}
}
:Note→ As Generic Type Example class implements interface A:
:It is taken as Type to create object:
interface A {
}
interface B<T> {
}
:OR:
interface B<T>extends A {
}
class Example<T> implements A,B<T> {
}
public class WildCards<T extends Example<? extends A> & & B<? extends A>>{
}
}
:Will generate error as Example implements B<T>:
:It will be considered as - B<? extends A> in both the cases:
:B<? extends A> & B<? extends A> cannot exists in same time:
interface A {
}
interface B {
}
class Example<T> implements A,B {
}
public class WildCards<T extends Example<? extends A> & B>{
public static void main(String[] args) {
WildCards<Example<A>> obj = new WildCards<>();
}
}
:Reason→As both the interfaces now considered as specific and different from each other:
interface A {
}
interface B<T> {
}
:OR:
interface B<T> extends A {
}
class Example<T> implements B<T> {
}
public class WildCards2 <T extends Example<? extends B<? extends A>>> {
public static void main(String[] args) {
WildCards2<Example<B<A>>> obj = new WildCards2<>();
}
}
:The above is inclusive nature of Wildcard in Generic Types:
:During generating object of Class ,:
:The type of Class should be the Class that implements the Interfaces:
interface A {
}
interface B<T extends A> {
}
class Example<T> implements B<A> {
}
class WildCards3 <T extends Example<? extends B<? extends A>>>{
public static void main(String[] args) {
WildCards3<Example<B<A>>> obj = new WildCards3<>();
obj.add(10, 20);
}
}
interface A {
}
interface B<T> {
}
interface C<T> {
}
class D<T> implements B<A>, C<A>{
}
class WildCards4<T extends B<? extends A> & C<? extends A>> {
public static void main(String[] args) {
WildCards4<D<A>> obj = new WildCards4<>();
}
}
interface A {
}
interface B<T extends A> {
}
interface C<T extends A> {
}
class D<T extends A> implements B<A>, C<A> {
}
class WildCards5 <T extends B<? extends A> & C<? extends A>> {
public static void main(String[] args) {
WildCards5<D<A>> obj = new WildCards5<>();
}
}
interface A{
}
interface B<T>{
}
class WildCards <T extends B<? extends A>> {
public static void main(String[] args) {
WildCards<B<A>> obj = new WildCards<>();
}
}
class A{
}
interface B<T>{
}
class WildCards <T extends B<? extends A>> {
public static void main(String[] args) {
WildCards<B<A>> obj = new WildCards<>();
}
}
Then any class having upper bound wildcard cannot have both the class and the interface, As the upper bound wildcard of interface represent the same as upper bound wildcard of the class and both cannot co exists . As Java does Type Erasure for type safety, what it does after compilation, all generic types are erased and the class implements interface and the super interface looks same in bytecode.
class WildCards <T extends Example<? extends T> & A<? extends T>> {
}
:As→Example<? extends T> = A<? extends T>:
:And A<? extends T> & A<? extends T> cannot co-exists.:
class WildCards <T extends Example<? extends T> & A<? extends T>> {
}
:As→Example<? extends T> = A<? extends T>:
:And A<? extends T> & A<? extends T> cannot co-exists.:
interface A {
}
interface B<T> {
}
class Example<T> implements A , B<T> {
//Creation of Object\'s
Example<String> e1 = new Example<String>();
Example<Integer> e2 = new Example<Integer>();
Example<Example<String>> e3 = new Example<Example<String>>();
}
public class WildCards6 <T extends B<? extends A>> {
public static void main(String[] args){
//Same Objects can be created in class WildCard6 java class
Example<String> e1 = new Example<String>();
Example<Integer> e2 = new Example<Integer>();
Example<Example<String>> e3 = new Example<Example<String>>();
// Creation of Object\'s of WildCards6
WildCards6<Example<A>> obj = new WildCards6<>();
}
}
interface A {
}
interface B<T> {
}
class Example<T> implements A , B<T> {
//Creation of Object\'s
Example<String> e1 = new Example<String>();
Example<Integer> e2 = new Example<Integer>();
Example<Example<String>> e3 = new Example<Example<String>>();
}
public class WildCards6 <T extends B<? extends A>> {
public static void main(String[] args){
//Same Objects can be created in class WildCard6 java class
Example<String> e1 = new Example<String>();
Example<Integer> e2 = new Example<Integer>();
Example<Example<String>> e3 = new Example<Example<String>>();
// Creation of Object\'s of WildCards6
WildCards6<Example<A>> obj = new WildCards6<>();
}
}
interface A {
}
interface B{
}
class Example<T> implements A,B {
//Creation of Object\'s
Example<String> e1 = new Example<String>();
Example<Integer> e2 = new Example<Integer>();
Example<Example<String>> e3 = new Example<Example<String>>();
}
class WildCards6 <T extends Example<? extends A> & B >{
public static void main(String[] args){
// Creation of Object\'s
WildCards6<Example<A>> obj = new WildCards6<>();
}
}
interface A {
}
interface B{
}
class Example<T> implements A,B {
//Creation of Object\'s
Example<String> e1 = new Example<String>();
Example<Integer> e2 = new Example<Integer>();
Example<Example<String>> e3 = new Example<Example<String>>();
}
class WildCards6 <T extends Example<? extends A> & B >{
public static void main(String[] args){
// Creation of Object\'s
WildCards6<Example<A>> obj = new WildCards6<>();
}
}
But if the interface is generic , then the generic class which implements the generic interface cannot be present at same time with the interface i.e. rule 1.
interface A{
}
interface B<T>{
}
class WildCards <T extends B<? extends A>>{
public static void main(String[] args) {
WildCards<B<A>> obj = new WildCards<>();
}
}
OR
class A{
}
interface B<T>{
}
class WildCards <T extends B<? extends A>>{
public static void main(String[] args) {
WildCards<B<A>> obj = new WildCards<>();
}
}
interface A{
}
interface B<T>{
}
class WildCards <T extends B<? extends A>>{
public static void main(String[] args) {
WildCards<B<A>> obj = new WildCards<>();
}
}
OR
class A{
}
interface B<T>{
}
class WildCards <T extends B<? extends A>>{
public static void main(String[] args) {
WildCards<B<A>> obj = new WildCards<>();
}
}
interface A{
}
interface B<T>{
}
class WildCards <T extends B<? extends A>>{
public static void main(String[] args) {
WildCards<B<A>> obj = new WildCards<>();
}
}
OR
interface A{
}
interface B<T>{
}
class E<T> implements B<T>{
}
class WildCards <T extends B<? extends A>>{
public static void main(String[] args) {
WildCards<E<A>> obj = new WildCards<>();
}
}
interface A{
}
interface B<T>{
}
class WildCards <T extends B<? extends A>>{
public static void main(String[] args) {
WildCards<B<A>> obj = new WildCards<>();
}
}
OR
interface A{
}
interface B<T>{
}
class E<T> implements B<T>{
}
class WildCards <T extends B<? extends A>>{
public static void main(String[] args) {
WildCards<E<A>> obj = new WildCards<>();
}
}
But if two Upper Bound WildCard joined by '&' [AND] i.e. constituting Multiple Upper Bound then the Generic Class implement both the interfaces must be used as 'Type' to create object i.e. rule no.2 is must .
interface A{
}
interface B<T>{
}
interface E<T> {
}
class WildCards <T extends E<? extends B<? extends A>>>{
public static void main(String[] args) {
WildCards<E<B<A>>> obj = new WildCards<>();
}
}
interface A{
}
interface B<T>{
}
interface E<T> {
}
class WildCards <T extends E<? extends B<? extends A>>>{
public static void main(String[] args) {
WildCards<E<B<A>>> obj = new WildCards<>();
}
}
If a generic class implements generic interfaces. Then the inner upper bound can be used with the generic class as Type to create object.
interface A{
}
interface B<T>{
}
interface E<T> {
}
class D<T> implements E<T>,B<T>, A{
}
class WildCards <T extends E<? extends B<? extends A>>>
{
public static void main(String[] args) {
WildCards<D<B<A>>> obj = new WildCards<>();
}
}
OR
class WildCards <T extends D<? extends B<? extends A>>>
//Here D is Class
{
public static void main(String[] args) {
WildCards<D<B<A>>> obj = new WildCards<>();
}
}
interface A{
}
interface B<T>{
}
interface E<T> {
}
class D<T> implements E<T>,B<T>, A{
}
class WildCards <T extends E<? extends B<? extends A>>>
{
public static void main(String[] args) {
WildCards<D<B<A>>> obj = new WildCards<>();
}
}
OR
class WildCards <T extends D<? extends B<? extends A>>>
//Here D is Class
{
public static void main(String[] args) {
WildCards<D<B<A>>> obj = new WildCards<>();
}
}
Now if there is Multiple Upper Bound present , it acts the same as above i.e. the inner upper bound can be used with the generic class as Type to create object.
interface A {
}
interface B<T> {
}
interface E<T> {
}
interface F<T> {
}
class D<T> implements E<T>,B<T>, F<T>,A{
}
class WildCards<T extends E<? extends B<? extends A>>
& F<? extends B<? extends A>>> {
public static void main(String[] args) {
WildCards<D<B<A>>> obj = new WildCards<>();
}
}
interface A {
}
interface B<T> {
}
interface E<T> {
}
interface F<T> {
}
class D<T> implements E<T>,B<T>, F<T>,A{
}
class WildCards<T extends E<? extends B<? extends A>>
& F<? extends B<? extends A>>> {
public static void main(String[] args) {
WildCards<D<B<A>>> obj = new WildCards<>();
}
}
But the generic class which implements the interfaces if put as multiple upper bound , then rule no.1 is implemented i.e. Generic Class which implemented the generic interface and the generic interface cannot co-exists in multiple upper bound wildcards.
interface A {
}
interface B<T> {
}
interface E<T> {
}
interface F<T> {
}
class D<T> implements E<T>,B<T>, F<T>,A{
}
Then
class WildCards<T extends D<? extends B<? extends A>>
&F<? extends B<? extends A>>> {}
cannot occur that is:
D<? extends B<? extends A>> = F<? extends B<? extends A>>
And both cannot co-exists.
interface A {
}
interface B<T> {
}
interface E<T> {
}
interface F<T> {
}
class D<T> implements E<T>,B<T>, F<T>,A{
}
Then
class WildCards<T extends D<? extends B<? extends A>>
&F<? extends B<? extends A>>> {}
cannot occur that is:
D<? extends B<? extends A>> = F<? extends B<? extends A>>
And both cannot co-exists.
class A{
}
class A1<T> {
}
interface B1{
}
interface C1{
}
class WildCards<T extends A1<? extends A & B1 & C1>>
= NOT ALLOWED
class A{
}
class A1<T> {
}
interface B1{
}
interface C1{
}
class WildCards<T extends A1<? extends A & B1 & C1>>
= NOT ALLOWED
class A1 {
}
class B1<T> {
}
class WildCards<T extends B1<? extends A1>>{
public static void main(String[] args) {
WildCards<B1<A1>> obj = new WildCards<>();
}
}
class A1 {
}
class B1<T> {
}
class WildCards<T extends B1<? extends A1>>{
public static void main(String[] args) {
WildCards<B1<A1>> obj = new WildCards<>();
}
}
Or an Interface and a generic class , then also it gives permission to create Upper Bound WildCards and its Object .
interface A1 {
}
class B1<T> {
}
class WildCards<T extends B1<? extends A1>>{
public static void main(String[] args) {
WildCards<B1<A1>> obj = new WildCards<>();
}
}
interface A1 {
}
class B1<T> {
}
class WildCards<T extends B1<? extends A1>>{
public static void main(String[] args) {
WildCards<B1<A1>> obj = new WildCards<>();
}
}
But when there are more than one interface, then we need a Class which implement those interfaces so that we can create its object using the Class as Type during multiple upper bound is applied else Type remains unknown and in such cases we use Unbounded Wild Cards .
interface A1 {
}
interface C1 <T>{
}
class B1<T> {
}
class WildCards<T extends B1<? extends A1> & C<? extends A1>>{
public static void main(String[] args) {
WildCards<TypeUnkown> obj = new WildCards<>();
//As TypeUnkown we cannot create Object
//hence here we use unbounded wild card.
}
}
OR
interface A1 {
}
interface B1<T>{
}
interface C1 <T>{
}
class WildCards<T extends B1<? extends A1> & C<? extends A1>>{
WildCards<TypeUnkown> obj = new WildCards<>();
//As TypeUnkown we cannot create Object
//hence here we use unbounded wild card.
}
}
interface A1 {
}
interface C1 <T>{
}
class B1<T> {
}
class WildCards<T extends B1<? extends A1> & C<? extends A1>>{
public static void main(String[] args) {
WildCards<TypeUnkown> obj = new WildCards<>();
//As TypeUnkown we cannot create Object
//hence here we use unbounded wild card.
}
}
OR
interface A1 {
}
interface B1<T>{
}
interface C1 <T>{
}
class WildCards<T extends B1<? extends A1> & C<? extends A1>>{
WildCards<TypeUnkown> obj = new WildCards<>();
//As TypeUnkown we cannot create Object
//hence here we use unbounded wild card.
}
}
Eg:
interface A{
}
interface B<T>{
}
interface C<T>{
}
abstract class A1<T> implements A,B<T>, C<T> {
}
class WildCards8<T extends A1<? extends A>> {
}
public static void main(String[] args) {
WildCards<A1<A>> obj = new WildCards<>();
}
}
Eg:
interface A{
}
interface B<T>{
}
interface C<T>{
}
abstract class A1<T> implements A,B<T>, C<T> {
}
class WildCards8<T extends A1<? extends A>> {
}
public static void main(String[] args) {
WildCards<A1<A>> obj = new WildCards<>();
}
}
interface A{
}
interface B<E> extends A{
}
interface C<E> extends B<E>{
}
class D implements C<A>{
}
class WildCards<T extends C<? extends D> & B<? extends D>>{
}
Then :
C<? extends D> = B<? extends D>
B<? extends D> and B<? extends D> cannot co-exist
interface A{
}
interface B<E> extends A{
}
interface C<E> extends B<E>{
}
class D implements C<A>{
}
class WildCards<T extends C<? extends D> & B<? extends D>>{
}
Then :
C<? extends D> = B<? extends D>
B<? extends D> and B<? extends D> cannot co-exist
interface A{}
interface A1<T>{}
abstract class B<T> implements A1<T>{}
class WildCardss<T extends B<? extends A> & A1<? extends A>> {}
Then :
B<? extends D> = A1<? extends A>
A1<? extends A> and A1<? extends A> cannot co-exist
interface A{}
interface A1<T>{}
abstract class B<T> implements A1<T>{}
class WildCardss<T extends B<? extends A> & A1<? extends A>> {}
Then :
B<? extends D> = A1<? extends A>
A1<? extends A> and A1<? extends A> cannot co-exist
java.util.List → is an Interface
java.util.ArrayList → A class which implements List Interface.
Therefore,
class A {}
:Now, Class A can be an Interface:
:i.e. Interface A:
class upperBoundWCEg1<T extends List<? extends ArrayList<A>>>{
public static void main(String[] args) {
upperBoundWCEg1<ArrayList<ArrayList<A>>> obj =
new upperBoundWCEg1<>();
}
}
Implementation of Rule : 11 , Where,
AbstractList is an Abstract Class which implements List interface,
cannot co-exist with List in upper bound wild card.
T extends Set<E>
Further:
T extends Set<? extends TreeSet<E>>
Where E is an Interface , in the Example.
T extends Set<E>
Further:
T extends Set<? extends TreeSet<E>>
Where E is an Interface , in the Example.
java.util.Set → Is an Interface
java.util.NavigableSet → Is an Interface
:And TreeSet is the class which implements NavigableSet:
:NavigableSet extends SortedSet interface:
:And SortedSet extends Set interface:
:Implementaion of Rule 10 and As :
:NavigableSet extends SortedSet interface:
:And SortedSet extends Set interface:
:Hence Set and NavigableSet cannot co-exist:
:In Upper Bound Wild Card:
T extends Map<K,V>
Further:
T extends Map<? extends TreeMap<E,F> ,
? extends TreeMap<E,F>>
Where E and F are Interfaces , in the Example.
And TreeMap is a Class which implements Map interface,
through SortedMap interface.
TreeMap also implements Navigable Map.
T extends Map<K,V>
Further:
T extends Map<? extends TreeMap<E,F> ,
? extends TreeMap<E,F>>
Where E and F are Interfaces , in the Example.
And TreeMap is a Class which implements Map interface,
through SortedMap interface.
TreeMap also implements Navigable Map.
T extends Map<K,V>
Further:
T extends Map<? extends TreeMap<K,V> ,
? extends TreeMap<K,V> >
Further:
T extends Map<? extends TreeMap<? extends I,? extends J> ,
? extends TreeMap<? extends I,? extends J>>
Where I and J are Interfaces , in the Example.
And TreeMap is a Class which implements Map interface,
through SortedMap interface.
TreeMap also implements Navigable Map.
T extends Map<K,V>
Further:
T extends Map<? extends TreeMap<K,V> ,
? extends TreeMap<K,V> >
Further:
T extends Map<? extends TreeMap<? extends I,? extends J> ,
? extends TreeMap<? extends I,? extends J>>
Where I and J are Interfaces , in the Example.
And TreeMap is a Class which implements Map interface,
through SortedMap interface.
TreeMap also implements Navigable Map.
Note :
If ,
T extends Map<? extends TreeMap<? extends I,? extends J> ,
? extends TreeMap<? extends I,? extends J>> &
NavigableMap<? extends TreeMap<? extends I,? extends J >,
? extends TreeMap<? extends I,? extends J > >
As Interface NavigableMap<K,V> extends Interface SortedMap<K,V>
And Interface SortedMap<K,V> extends Interface Map<K,V>
Hence it will throw error as Both cannot co-exist as per Rule 10.
A parameter "t" of type T, where T is a generic type that extends a List of elements that are subtypes of Number. This means that the parameter "t" could be a List of any type of Number, such as Integer, Double, Float, etc. That is : List < Integer > , List < Double > , List < Float > etc. is the bound for List < ? extends Number > .
This means that the first parameter, "t", is a List of elements that are subtypes of String. That is : List < String > .
Note :Now its Same for Float, Double ,..., etc. That is for: List < ? extends Float > , List < ? extends Double > , ... etc.
This is a type parameter for a generic class or method in Java. The type parameter "T" is bounded by the interface "Set" and it is further specified that the type of elements in this Set must be a subclass of "Number". In other words, this type parameter "T" represents a Set of elements, where the elements must be of type Number or a subclass of Number.A subclass of "Number" in Java is a class that extends the "Number" class. The "Number" class is a superclass for several subclasses in Java such as "Integer", "Double", "Float", "Long", etc. Here 't' is the parameter of the method. It is of type T, which is a Set of elements that are a subclass of "Number". That is : Set < Integer > , Set < Double > , Set < Float > etc. is the bound for Set < ? extends Number > .
Note : What Does < T extends Map < ? extends String, ? extends Number > > returnType FuncName(T t) mean?
This is a type parameter for a generic class or method in Java. The type parameter "T" is bounded by the interface "Map" and it is further specified that the type of elements in this Map i.e. it's Keys must be sub types of String class and Values must be a subclass of "Number" . In other words, this type parameter "T" represents a Map of elements constitute of Keys and Values, where the Keys must be of type String or a subclass of String and Values must be of type Number or a subclass of Number.A subclass of "Number" in Java is a class that extends the "Number" class. The "Number" class is a superclass for several subclasses in Java such as "Integer", "Double", "Float", "Long", etc. Here 't' is the parameter of the method. It is of type T, which is a Map of elements. That is : Map < String, Integer > , Map < String, Float > etc. is the bound for Map < ? extends String, ? extends Number > .
This method takes a list of objects that are instances of or subclasses of the Number class. The wildcard ? extends Number specifies that the list can contain elements of any type that is a subclass of Number, including Number itself. Since this method has a void return type, it doesn't return the list.
This method takes a list of objects that are instances of or subclasses of the Number class. The wildcard ? extends Number specifies that the list can contain elements of any type that is a subclass of Number, including Number itself. List < ? extends Number > is return type , hence returns the same list.
Note: We know List < Type/Object > can create List which can be editable and alterable i.e. we can add and remove during runtime. Similarly for Set < Type/Object > , Map < Key , Value > but when we say List < ? extends Type > , Set < ? extends Type > and Map < ? extends Key , ? extends Value > i.e. their type information are not available at runtime i.e. not reifiable, hence not alterable and editable. Therefore we cannot add , remove , any element during runtime. Hence, we can use Set.of() and Map.of() to create unmodifiable Set and Map and Arrays.asList() to convert an array into a List object , as they take constant elements according to their types in a fixed compile time .
Note: Rule 1 , 10, 11 of Upper Bound WildCard i.e. based on TypeErasure may get bypassed by some IDE like IntelliJ IDEA and some online editor like ONLINE GDB COMPILER and may get compiled successfully, the above rules are set based on IDE : VS CODE and ECLIPSE IDE .
- 1. T extends Set < ? extends Number > & NavigableSet < ? extends Number > -Eg 1
- 2. T extends Map extends String, ? extends Number > & NavigableMap < ? extends String, ? extends Number > -Eg 2
interface A1{ }
interface B1<T> { }
interface C1<T> extends B1<T>{ }
class D1<T> implements C1<T>,A1{ }
public class Example4 <T extends B1<? extends A1> & C1<? extends A1>>{
public static void main(String[] args) {
Example4<D1<A1>> d = new Example4<>();
}
}
OR
class/abstract class A1{ }
[i.e. Class or Abstract Class]
interface B1<T> {}
interface C1<T> extends B1<T>{ }
class D1<T> extends A1 implements C1<T> { }
public class Example4 <T extends B1<? extends A1> & C1<? extends A1>>{
public static void main(String[] args) {
Example4<D1<A1>> d = new Example4<>();
}
}
interface A1{ }
interface B1<T> { }
interface C1<T> extends B1<T>{ }
class D1<T> implements C1<T>,A1{ }
public class Example4 <T extends B1<? extends A1> & C1<? extends A1>>{
public static void main(String[] args) {
Example4<D1<A1>> d = new Example4<>();
}
}
OR
class/abstract class A1{ }
[i.e. Class or Abstract Class]
interface B1<T> {}
interface C1<T> extends B1<T>{ }
class D1<T> extends A1 implements C1<T> { }
public class Example4 <T extends B1<? extends A1> & C1<? extends A1>>{
public static void main(String[] args) {
Example4<D1<A1>> d = new Example4<>();
}
}
Note: If anyone see that NavigableSet extends Set interface , hence NavigableSet implements all the functions of Set , hence in VSCODE compiler compiles and while converting into bytecode erases the Type and it shows NavigableSet is put two times hence the implementation T extends Set < ? extends Number > & NavigableSet < ? extends Number > = T extends NavigableSet < ? extends Number > . This is same for Map and NavigableMap .
- 1. If a generic class implements generic interface then during multiple upper bound including lower bound wild card implemented , cannot co-exist , it causes type erasure - same like Upper Bound .
Description : In the Java programming language, a lower bound is a type that specifies that a type parameter must be either the specified type or a supertype of the specified type. Lower bounds are specified using the super keyword followed by the type that you want to use as the lower bound.
'Super' keyword is used to refer and invoke parent class's properties . But unlike upper bound 'extend' , we cannot just call 'Super' after Type 'T' i.e. `T super AnyClass/Interface` . It generally comes with the `?` wildcard i.e. what we called `Lower Bound Wild Card`.
Explanation : To explain about Lower Bound Wild Card , lets take example of Upper Bound Wild Card : < T extends List < ? extends Number > >, here , T extends List falls under Upper Bound where `Type is bound to List` and we know ArrayList class extends List, hence say ,the Type is ArrayList then Object of Type `T` can access all of its predefined methods .
<T extends List <Number>> void test (T type, Integer a){
type.add(a); //Here `type` is Object of T
}
//Say T is ArrayList which `Extends` List
//formal object reference 'type`will be ,
//replaced by the Object of ArrayList
//That is:
ArrayList<Integer> al = new ArrayList<>();
test(al,1);
//That is al.add(1)
<T extends List <Number>> void test (T type, Integer a){
type.add(a); //Here `type` is Object of T
}
//Say T is ArrayList which `Extends` List
//formal object reference 'type`will be ,
//replaced by the Object of ArrayList
//That is:
ArrayList<Integer> al = new ArrayList<>();
test(al,1);
//That is al.add(1)
Now, for < T extends List < ? extends Number > > , < ? extends Number > ,is known as Upper Bound WildCard . Due to the use of `?` Wild Card , Compiler could not recognise which subclass of Number will be there for `?` during run time , hence type information remain unavailable i.e. not reifiable ,hence we cannot alter without knowing its type. Hence:
<T extends List <? extends Number>> void test (T type, Integer a){
type.add(a); //Cannot takes place it will throw Error
type.remove(a); //Cannot takes place it will throw Error
}
<T extends List <? extends Number>> void test (T type, Integer a){
type.add(a); //Cannot takes place it will throw Error
type.remove(a); //Cannot takes place it will throw Error
}
And we know that : ArrayList < Integer > , ArrayList < Float >, ArrayList < Double > ...etc. are all bound to < T extends List < ? extends Number > > , since Integer extends Number , Float extends Number, Double extends Number ...etc . Here Integer, Float , Double ...etc. are all sub-classes or child-classes of Number abstract class and after the addition and removal i.e. alteration of elements , the objects of ArrayList < Integer > , ArrayList < Float >, ArrayList < Double > ...etc. passed to the method as actual parameter .As it takes constant elements according to their types in a fixed compile time .
<T extends List <? extends Number>> void test (T type){}
ArrayList<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.remove(1);
test(intList);
<T extends List <? extends Number>> void test (T type){}
ArrayList<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.remove(1);
test(intList);
Now, Lower Bound Wild Card i.e. < T extends List < ? super Number > > , super keyword will fetch all elements that contains inside the class Number. Abstract Class Number have abstract methods which returns byte, double, float, int, long, and short. Moreover the abstract class Number is the superclass/parent class of platform classes(Wrapper Classes :Integer, Float, Byte,Double,Long and Short) representing numeric values that are convertible to the primitive types byte, double, float, int, long, and short. Hence now `Compiler` knows what type of information needed i.e. Number which implies Integer, Float, Byte,Double,Long and Short or any of its supertypes, such as Object[java.lang.Object] and Serializable[java.io.Serializable] . As we know Object class is superclass/ parent class of all classes and abstract class Number implements java.io.Serializable interface. Hence now it is reifiable : `Now information available at runtime. And can be alterable at runtime.` .
<T extends List <? super Number>> void test (T type){
t.add(1);
t.add(2);
t.add(3);
t.remove(1);
}
//Now it is possible to alter during runtime .
ArrayList<Number>list = new ArrayList<>();
ArrayList<Object> list1 = new ArrayList<>();
ArrayList<Serializable> list2 = new ArrayList<>();
test(list);
test(list1);
test(list2);
<T extends List <? super Number>> void test (T type){
t.add(1);
t.add(2);
t.add(3);
t.remove(1);
}
//Now it is possible to alter during runtime .
ArrayList<Number>list = new ArrayList<>();
ArrayList<Object> list1 = new ArrayList<>();
ArrayList<Serializable> list2 = new ArrayList<>();
test(list);
test(list1);
test(list2);
interface A{
}
interface A1<T>{
}
class B<T> implements A1<T>,A{
}
public class JavaWild1 <T extends B<? super A> & A1< ? super A>>{}
:As→B<? super A> = A1< ? super A>:
:And A1< ? super A> & A1< ? super A> cannot co-exists.:
interface A{
}
interface A1<T>{
}
class B<T> implements A1<T>,A{
}
public class JavaWild1 <T extends B<? super A> & A1< ? super A>>{}
:As→B<? super A> = A1< ? super A>:
:And A1< ? super A> & A1< ? super A> cannot co-exists.:
interface A {
}
interface C extends A{
}
interface A1<T>{
}
class JavaWild1 <T extends A1<? super C>>{
public static void main(String[] args) {
JavaWild1<A1<C>> jw1 = new JavaWild1<>();
JavaWild1<A1<A>> jw2 = new JavaWild1<>();
}
}
Note→ A is Super interface of C.
Hence , both C and A are used as Type to create Object.
interface A {
}
interface C extends A{
}
interface A1<T>{
}
class JavaWild1 <T extends A1<? super C>>{
public static void main(String[] args) {
JavaWild1<A1<C>> jw1 = new JavaWild1<>();
JavaWild1<A1<A>> jw2 = new JavaWild1<>();
}
}
Note→ A is Super interface of C.
Hence , both C and A are used as Type to create Object.
class A{
}
class B extends A{
}
class C<T> extends B {
}
public class JavaWild2<T extends C<? super B>> {
public static void main(String[] args) {
JavaWild2<C<B>> jw1 = new JavaWild2<>();
JavaWild2<C<A>> jw2 = new JavaWild2<>();
}
}
Note→ A is Super Class of B.
Hence , both B and A are used as Type to create Object.
class A{
}
class B extends A{
}
class C<T> extends B {
}
public class JavaWild2<T extends C<? super B>> {
public static void main(String[] args) {
JavaWild2<C<B>> jw1 = new JavaWild2<>();
JavaWild2<C<A>> jw2 = new JavaWild2<>();
}
}
Note→ A is Super Class of B.
Hence , both B and A are used as Type to create Object.
interface A{}
interface B extends A{}
class C<T> implements B{}
public class JavaWild3<T extends C<? super B>>{
public static void main(String[] args) {
JavaWild3<C<B>> jw1 = new JavaWild3<>();
JavaWild3<C<A>> jw2 = new JavaWild3<>();
}
}
Note→ A is Super Interface of B.
Hence , both B and A are used as Type to create Object.
interface A{}
interface B extends A{}
class C<T> implements B{}
public class JavaWild3<T extends C<? super B>>{
public static void main(String[] args) {
JavaWild3<C<B>> jw1 = new JavaWild3<>();
JavaWild3<C<A>> jw2 = new JavaWild3<>();
}
}
Note→ A is Super Interface of B.
Hence , both B and A are used as Type to create Object.
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> {}
interface C1<T> {}
class A2<T> implements A1<T>,C1<T> {}
public class JavaWild4 <T extends A1<? super B>
& C1<? super B>> {
public static void main(String[] args) {
JavaWild4<A2<A>> jw1 = new JavaWild4<>();
JavaWild4<A2<B>> jw2 = new JavaWild4<>();
JavaWild4<A2<C>> jw3 = new JavaWild4<>();
}
}
Note→ A is Super Interface of B.
And, C is Super Interface of B.
And, Including B itself.
Hence , both B,C and A are used as Type to create Object.
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> {}
interface C1<T> {}
class A2<T> implements A1<T>,C1<T> {}
public class JavaWild4 <T extends A1<? super B>
& C1<? super B>> {
public static void main(String[] args) {
JavaWild4<A2<A>> jw1 = new JavaWild4<>();
JavaWild4<A2<B>> jw2 = new JavaWild4<>();
JavaWild4<A2<C>> jw3 = new JavaWild4<>();
}
}
Note→ A is Super Interface of B.
And, C is Super Interface of B.
And, Including B itself.
Hence , both B,C and A are used as Type to create Object.
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> {}
interface C1<T> {}
class A2<T> implements A1<T>, C1<T> {}
public class JavaWild5 <T extends A1<? super C>
& C1<? super B>>{
public static void main(String[] args) {
JavaWild5<A2<A>> jw1 = new JavaWild5<>();
JavaWild5<A2<C>> jw3 = new JavaWild5<>();
}
}
Note→ A is Super Interface of B.
And, C is Super Interface of B.
And, But B is not super interface of C.
Hence B Cannot be taken as Type.
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> {}
interface C1<T> {}
class A2<T> implements A1<T>, C1<T> {}
public class JavaWild5 <T extends A1<? super C>
& C1<? super B>>{
public static void main(String[] args) {
JavaWild5<A2<A>> jw1 = new JavaWild5<>();
JavaWild5<A2<C>> jw3 = new JavaWild5<>();
}
}
Note→ A is Super Interface of B.
And, C is Super Interface of B.
And, But B is not super interface of C.
Hence B Cannot be taken as Type.
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface C1<T> {}
class A2<T> implements C1<T>{}
public class JavaWild6 <T extends C1<? super A1<? super B>>> {
public static void main(String[] args) {
JavaWild6<A2<A>> jw1 = new JavaWild6<>();
JavaWild6<A2<B>> jw2 = new JavaWild6<>();
JavaWild6<A2<C>> jw3 = new JavaWild6<>();
JavaWild6<A2<A1>> jw4 = new JavaWild6<>();
JavaWild6<C1<A>> jw5 = new JavaWild6<>();
JavaWild6<C1<B>> jw6 = new JavaWild6<>();
JavaWild6<C1<C>> jw7 = new JavaWild6<>();
JavaWild6<C1<A1>> jw8 = new JavaWild6<>();
}
}
Note → Super types of A1 is: B , C, A and A1.
To implement A1 as type i.e. A1 we cannot implement as Generic ,
i.e. Cannot be Parameterized , which will generate error,
i.e. A1<A>, A1<B> cannot be used, where A,B is super of B.
Rather than we implement it as RAW type and will ,
generate Warning of saying Parameterized.
As,
JavaWild6<A2<A1>> jw4 = new JavaWild6<>();
JavaWild6<C1<A1>> jw8 = new JavaWild6<>();
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface C1<T> {}
class A2<T> implements C1<T>{}
public class JavaWild6 <T extends C1<? super A1<? super B>>> {
public static void main(String[] args) {
JavaWild6<A2<A>> jw1 = new JavaWild6<>();
JavaWild6<A2<B>> jw2 = new JavaWild6<>();
JavaWild6<A2<C>> jw3 = new JavaWild6<>();
JavaWild6<A2<A1>> jw4 = new JavaWild6<>();
JavaWild6<C1<A>> jw5 = new JavaWild6<>();
JavaWild6<C1<B>> jw6 = new JavaWild6<>();
JavaWild6<C1<C>> jw7 = new JavaWild6<>();
JavaWild6<C1<A1>> jw8 = new JavaWild6<>();
}
}
Note → Super types of A1 is: B , C, A and A1.
To implement A1 as type i.e. A1 we cannot implement as Generic ,
i.e. Cannot be Parameterized , which will generate error,
i.e. A1<A>, A1<B> cannot be used, where A,B is super of B.
Rather than we implement it as RAW type and will ,
generate Warning of saying Parameterized.
As,
JavaWild6<A2<A1>> jw4 = new JavaWild6<>();
JavaWild6<C1<A1>> jw8 = new JavaWild6<>();
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface A2<T> extends A1<T> {}
interface C1<T> {}
class A3<T> implements C1<T>, A2<T> {}
public class JavaWild8 <T extends C1<? super A1<? super B>> &
A2<? super A1<? super C>>>{
public static void main(String[] args) {
JavaWild8<A3<A>> jw1 = new JavaWild8<>();
JavaWild8<A3<B>> jw2 = new JavaWild8<>();
JavaWild8<A3<C>> jw3 = new JavaWild8<>();
JavaWild8<A3<A1>> jw4 = new JavaWild8<>();
}
}
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface A2<T> extends A1<T> {}
interface C1<T> {}
class A3<T> implements C1<T>, A2<T> {}
public class JavaWild8 <T extends C1<? super A1<? super B>> &
A2<? super A1<? super C>>>{
public static void main(String[] args) {
JavaWild8<A3<A>> jw1 = new JavaWild8<>();
JavaWild8<A3<B>> jw2 = new JavaWild8<>();
JavaWild8<A3<C>> jw3 = new JavaWild8<>();
JavaWild8<A3<A1>> jw4 = new JavaWild8<>();
}
}
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface A2<T> extends A1<T> {}
interface C1<T> {}
class A3<T> implements C1<T> {}
public class JavaWild10<T extends C1<? extends A1<? super B>>>
{
public static void main(String[] args) {
JavaWild10<C1<A1<B>>> jw1 = new JavaWild10<>();
JavaWild10<C1<A1<C>>> jw2 = new JavaWild10<>();
JavaWild10<C1<A1<A>>> jw3 = new JavaWild10<>();
JavaWild10<A3<A1<A>>> jw4 = new JavaWild10<>();
JavaWild10<A3<A1<B>>> jw5 = new JavaWild10<>();
JavaWild10<A3<A1<C>>> jw6 = new JavaWild10<>();
}
}
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface A2<T> extends A1<T> {}
interface C1<T> {}
class A3<T> implements C1<T> {}
public class JavaWild10<T extends C1<? extends A1<? super B>>>
{
public static void main(String[] args) {
JavaWild10<C1<A1<B>>> jw1 = new JavaWild10<>();
JavaWild10<C1<A1<C>>> jw2 = new JavaWild10<>();
JavaWild10<C1<A1<A>>> jw3 = new JavaWild10<>();
JavaWild10<A3<A1<A>>> jw4 = new JavaWild10<>();
JavaWild10<A3<A1<B>>> jw5 = new JavaWild10<>();
JavaWild10<A3<A1<C>>> jw6 = new JavaWild10<>();
}
}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface A2<T> extends A1<T> {}
interface C1<T> {}
class A3<T> implements C1<T> {
}
public class JavaWild10<T extends C1<? super A1<? extends C>>>
{
public static void main(String[] args) {
JavaWild10<A3<A>> jw1 = new JavaWild10<>();
JavaWild10<A3<B>> jw2 = new JavaWild10<>();
JavaWild10<A3<C>> jw3 = new JavaWild10<>();
JavaWild10<A3<A1>> jw4 = new JavaWild10<>();
JavaWild10<C1<A>> jw5 = new JavaWild10<>();
JavaWild10<C1<B>> jw6 = new JavaWild10<>();
JavaWild10<C1<C>> jw7 = new JavaWild10<>();
JavaWild10<C1<A1>> jw8 = new JavaWild10<>();
}
}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface A2<T> extends A1<T> {}
interface C1<T> {}
class A3<T> implements C1<T> {
}
public class JavaWild10<T extends C1<? super A1<? extends C>>>
{
public static void main(String[] args) {
JavaWild10<A3<A>> jw1 = new JavaWild10<>();
JavaWild10<A3<B>> jw2 = new JavaWild10<>();
JavaWild10<A3<C>> jw3 = new JavaWild10<>();
JavaWild10<A3<A1>> jw4 = new JavaWild10<>();
JavaWild10<C1<A>> jw5 = new JavaWild10<>();
JavaWild10<C1<B>> jw6 = new JavaWild10<>();
JavaWild10<C1<C>> jw7 = new JavaWild10<>();
JavaWild10<C1<A1>> jw8 = new JavaWild10<>();
}
}
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface A2<T> extends A1<T> {}
interface C1<T> {}
class A3<T> implements C1<T>, A2<T> {}
public class JavaWild9<T extends C1<? extends A1<? super B>>
& A2<? extends A1<? super C>>>
{
public static void main(String[] args) {
JavaWild9<A3<A1<A>>> jw1 = new JavaWild9<>();
}
}
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface A2<T> extends A1<T> {}
interface C1<T> {}
class A3<T> implements C1<T>, A2<T> {}
public class JavaWild9<T extends C1<? extends A1<? super B>>
& A2<? extends A1<? super C>>>
{
public static void main(String[] args) {
JavaWild9<A3<A1<A>>> jw1 = new JavaWild9<>();
}
}
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface A2<T> extends A1<T> {}
interface C1<T> {}
class A3<T> implements C1<T>, A2<T> {
}
public class JavaWild9<T extends C1<? super A1<? extends B>>
& A2<? super A1<? extends C>>> {
public void add () {
System.out.println("int");
}
public static void main(String[] args) {
JavaWild9<A3<A>> jw1 = new JavaWild9<>();
JavaWild9<A3<B>> jw2 = new JavaWild9<>();
JavaWild9<A3<C>> jw3 = new JavaWild9<>();
JavaWild9<A3<A1>> jw4 = new JavaWild9<>();
}
}
interface A {}
interface C extends A {}
interface B extends C {}
interface A1<T> extends B {}
interface A2<T> extends A1<T> {}
interface C1<T> {}
class A3<T> implements C1<T>, A2<T> {
}
public class JavaWild9<T extends C1<? super A1<? extends B>>
& A2<? super A1<? extends C>>> {
public void add () {
System.out.println("int");
}
public static void main(String[] args) {
JavaWild9<A3<A>> jw1 = new JavaWild9<>();
JavaWild9<A3<B>> jw2 = new JavaWild9<>();
JavaWild9<A3<C>> jw3 = new JavaWild9<>();
JavaWild9<A3<A1>> jw4 = new JavaWild9<>();
}
}
interface A {}
interface B<T> extends A {}
interface C<T> extends B<T> {}
class D implements C<A> {}
public class JavaWild11<T extends C<? super D> & B<? super D>>{
}
interface A {}
interface B<T> extends A {}
interface C<T> extends B<T> {}
class D implements C<A> {}
public class JavaWild11<T extends C<? super D> & B<? super D>>{
}
Note: Generic interface C extends B , hence super or parent is B , during compilation the byte code and erase the sub interface and impose error for type safety which also known as Type Erasure.
interface A {}
interface B<T> extends A {}
interface C<T> extends B<T> {}
abstract class D<T> implements C<T>{}
class JavaWild11<T extends D<? super A> & C<? super A>>{
}
class JavaWild12<T extends D<? super A> & B<? super A>>{
}
[Explanation:]
D<? super A> = C<? super A>
C<? super A> = C<? super A> cannot co-exist.
D<? super A> = B<? super A>
B<? super A> = B<? super A> cannot co-exist.
interface A {}
interface B<T> extends A {}
interface C<T> extends B<T> {}
abstract class D<T> implements C<T>{}
class JavaWild11<T extends D<? super A> & C<? super A>>{
}
class JavaWild12<T extends D<? super A> & B<? super A>>{
}
[Explanation:]
D<? super A> = C<? super A>
C<? super A> = C<? super A> cannot co-exist.
D<? super A> = B<? super A>
B<? super A> = B<? super A> cannot co-exist.
T extends List<? super ArrayList<E>>
Where E is a Class , in the Example.
Here in the Example:
ArrayList<AbstractList<E>>,ArrayList<ArrayList<E>>,
ArrayList<Object> are bound to
T extends List<? extends ArrayList<E>>
And AbstractList, Object , and ArrayList
are super types of ArrayList .
T extends List<? super ArrayList<E>>
Where E is a Class , in the Example.
Here in the Example:
ArrayList<AbstractList<E>>,ArrayList<ArrayList<E>>,
ArrayList<Object> are bound to
T extends List<? extends ArrayList<E>>
And AbstractList, Object , and ArrayList
are super types of ArrayList .
T extends List<? super ArrayList<E>>
Where E is an Interface , in the Example.
Here in the Example:
ArrayList<AbstractList<E>>,ArrayList<ArrayList<E>>,
ArrayList<Object> are bound to
T extends List<? extends ArrayList<E>>
And AbstractList, Object , and ArrayList
are super types of ArrayList .
T extends List<? super ArrayList<E>>
Where E is an Interface , in the Example.
Here in the Example:
ArrayList<AbstractList<E>>,ArrayList<ArrayList<E>>,
ArrayList<Object> are bound to
T extends List<? extends ArrayList<E>>
And AbstractList, Object , and ArrayList
are super types of ArrayList .
T extends Set<? super TreeSet<E>>
Where E is an Interface , in the Example.
Here in the Example:
TreeSet<TreeSet<E>>,TreeSet<Object>,
TreeSet<Set<E>> , TreeSet<AbstractSet<E>>,
TreeSet<NavigableSet<E>> and TreeSet<SortedSet<E>>
are bound to T extends Set<? super TreeSet<E>>
And TreeSet, Object , Set , AbstractSet,
AbstractSet, NavigableSet and SortedSet
are super types of TreeSet .
T extends Set<? super TreeSet<E>>
Where E is an Interface , in the Example.
Here in the Example:
TreeSet<TreeSet<E>>,TreeSet<Object>,
TreeSet<Set<E>> , TreeSet<AbstractSet<E>>,
TreeSet<NavigableSet<E>> and TreeSet<SortedSet<E>>
are bound to T extends Set<? super TreeSet<E>>
And TreeSet, Object , Set , AbstractSet,
AbstractSet, NavigableSet and SortedSet
are super types of TreeSet .
T extends Map<? super TreeMap<E, F>, ? super TreeMap<E, F>>
Where E and F are Interfaces , in the Example.
Here in the Example:
TreeMap<TreeMap<E, F>, TreeMap<E, F>> ,
TreeMap<Object, Object> ,
TreeMap<Map<E, F>, Map<E, F>> ,
TreeMap<SortedMap<E, F>, SortedMap<E, F>> ,
TreeMap<NavigableMap<E, F>, NavigableMap<E, F>> and
TreeMap<AbstractMap<E, F>, AbstractMap<E, F>>
are bound to :
T extends Map<? super TreeMap<E, F>, ? super TreeMap<E, F>>
And TreeMap, Object , Map, SortedMap,
NavigableMap, and AbstractMap
are super types of TreeMap .
T extends Map<? super TreeMap<E, F>, ? super TreeMap<E, F>>
Where E and F are Interfaces , in the Example.
Here in the Example:
TreeMap<TreeMap<E, F>, TreeMap<E, F>> ,
TreeMap<Object, Object> ,
TreeMap<Map<E, F>, Map<E, F>> ,
TreeMap<SortedMap<E, F>, SortedMap<E, F>> ,
TreeMap<NavigableMap<E, F>, NavigableMap<E, F>> and
TreeMap<AbstractMap<E, F>, AbstractMap<E, F>>
are bound to :
T extends Map<? super TreeMap<E, F>, ? super TreeMap<E, F>>
And TreeMap, Object , Map, SortedMap,
NavigableMap, and AbstractMap
are super types of TreeMap .
This means that this method takes in a generic argument of type T, where T must extend a List of elements that are either of type Number or a superclass of Number. The super classes of Numbers are Object and Serializable. In other words, the method accepts a list of objects that are either of type Number or a superclass of Number. Here 't' is the parameter of the method. It is of type T, which is a List of elements that is a Number or superclass of "Number".
public static < T extends List < ? super Number > > T test(T t, Integer a) {
for (Object n : t) {
System.out.println(n);
}
}
Here what type of element we have input is unknown ,
till we run the program i.e. in runtime , hence object
of an Object class is passed.
Explanation:The method takes a generic type parameter "T" that extends List of some type that is a superclass of String and it takes a parameter which is an object of type "T" . "T extends List " which means the "Type T" can take subclasses of List such as ArrayList.
Here String,Object,Serializable,Comparable<String> and CharSequence
Are Super Types of String.
Where as ArrayList<String>, ArrayList<Object>, ArrayList<Serializable>,
ArrayList<Comparable<String>>, and ArrayList<CharSequence> are bound to
T extends List < ? super String > > .
And ArrayList is subclass/childclass of List.
Which satisfies T extends List .
Here String,Object,Serializable,Comparable<String> and CharSequence
Are Super Types of String.
Where as ArrayList<String>, ArrayList<Object>, ArrayList<Serializable>,
ArrayList<Comparable<String>>, and ArrayList<CharSequence> are bound to
T extends List < ? super String > > .
And ArrayList is subclass/childclass of List.
Which satisfies T extends List .
Explanation: It defines a generic method that takes a parameter of a type T, which must be a subtype of Set and can contain any superclass of Number. The method returns the same type T.In other words, the method can accept any type of Set, such as HashSet < Serializable >, HashSet < Number > and HashSet < Object > i.e. It accept a Number or its supertype. The method returns the same type T that was passed in as a parameter.
Here Serializable,Number,Serializable,and Object are Super Types of Number.
Where as HashSet<Serializable>, HashSet<Number> and HashSet<Object>
are bound to T extends Set < ? super Number > .
And HashSet is subclass/childclass of Set.
Which satisfies T extends Set.
Here Serializable,Number,Serializable,and Object are Super Types of Number.
Where as HashSet<Serializable>, HashSet<Number> and HashSet<Object>
are bound to T extends Set < ? super Number > .
And HashSet is subclass/childclass of Set.
Which satisfies T extends Set.
Explanation: The generic method that takes a single argument of a type T, which must be a subtype(Sub Classes) of Map with keys of type String or a supertype of String, and values of type Number or a subtype of Number. The method returns the same type T that was passed in as an argument.
We already know:
Serializable,Number,Serializable,and Object are Super Types of Number.
String,Object,Serializable,Comparable<String> and CharSequence
Are Super Types of String.
Where as , HashMap<String, Number>, HashMap<String, Serializable>
...etc. are bound to T extends Map<? super String, ? super Number> .
And HashMap is subclass/childclass of Map.
Which satisfies T extends Map.
We already know:
Serializable,Number,Serializable,and Object are Super Types of Number.
String,Object,Serializable,Comparable<String> and CharSequence
Are Super Types of String.
Where as , HashMap<String, Number>, HashMap<String, Serializable>
...etc. are bound to T extends Map<? super String, ? super Number> .
And HashMap is subclass/childclass of Map.
Which satisfies T extends Map.
Explanation: `void` indicates that the function does not return any value."funcName" is the name of the function. "List" is a generic interface in Java that represents an ordered collection of elements. " < ? super Number > " is a bounded wildcard type that specifies that the list can contain elements of any type that is a superclass of the Number class. This means that the list can contain objects of type Number or any of its subclasses (such as Integer, Double, etc.). As we know it takes Number and its super types of abtsract Number class i.e. Serializable and Object.
Explanation: The method signature " List < ? super Number > funcName ( List < ? super Number > num)" declares a method named "funcName" that takes in a list of elements that are either of type Number or a superclass of Number, and returns a List of elements that are also either of type Number or a superclass of Number. The keyword "super" is used to denote a lower bound on the type parameter, which means that the list can contain elements of the specified type or any of its superclasses.
Note: When we say List < ? super Type > , Set < ? super Type > and Map < ? super Key , ? super Value > i.e. their type information are available at runtime i.e. reifiable, hence alterable and editable. As super keyword calls the same Class and its super type classes.Therefore we can add , remove , any element during runtime.
- T extends Map < ? super String, ? super Number > & NavigableMap < ? super String, ? super Number > -Eg
interface A2{ }
interface B2<T> {}
interface C2<T> extends B2<T>{ }
class D2<T> implements C2<T>,A2{ }
public class Example6 <T extends B2<? super A2> & C2<? super A2>>{
public static void main(String[] args) {
Example6<D2<A2>> d = new Example6<>();
}
}
OR
class/abstract class A2{ }
[i.e. Class or Abstract Class]
interface B2<T> {}
interface C2<T> extends B2<T>{ }
class D2<T> extends A2 implements C2<T> { }
public class Example6 <T extends B2<? super A2> & C2<? super A2>>{
public static void main(String[] args) {
Example6<D2<A2>> d = new Example6<>();
}
}
interface A2{ }
interface B2<T> {}
interface C2<T> extends B2<T>{ }
class D2<T> implements C2<T>,A2{ }
public class Example6 <T extends B2<? super A2> & C2<? super A2>>{
public static void main(String[] args) {
Example6<D2<A2>> d = new Example6<>();
}
}
OR
class/abstract class A2{ }
[i.e. Class or Abstract Class]
interface B2<T> {}
interface C2<T> extends B2<T>{ }
class D2<T> extends A2 implements C2<T> { }
public class Example6 <T extends B2<? super A2> & C2<? super A2>>{
public static void main(String[] args) {
Example6<D2<A2>> d = new Example6<>();
}
}
Note: If anyone see that NavigableMap extends Map interface , hence NavigableMap implements all the functions of Map , hence in VSCODE compiler compiles and while converting into bytecode erases the Type and it shows NavigableMap is put two times hence the implementation T extends Map < ? super String, ? super Number > & NavigableMap < ? super String, ? super Number > = T extends NavigableMap < ? super String, ? super Number > . And it put a difference between two IDE between IntelliJ and VSCODE.
Unbounded WildCard: Unbounded means here not bounded by any Upper Bound "extends" or Lower Bound "super" . Then the Type is only bounded by WildCard "?" which enables all types of reference to be passed in parameter. It accepts everything dynamically to be passed in type during runtime . Hence it is known as "Unbounded WildCards" .
Note : When we donot know what would be the type to pass in parameter , suppose there are two interfaces but there is no class that implement the interfaces and both the interfaces are bounded by a Generic class, hence the only option remains to create object of that class is using "< ? >" unbounded wild card.
interface A{}
interface B<T>{}
interface C<T>{}
class WildCards< T extends B<? extends A> & C<? super A>> {
public static void main(String[] args) {
WildCards<?> a = new WildCards<>();
}
}
Here we see there is no class exist ,
that implement interfaces B and C.
Hence "Wild Card" is must here.
interface A{}
interface B<T>{}
interface C<T>{}
class WildCards< T extends B<? extends A> & C<? super A>> {
public static void main(String[] args) {
WildCards<?> a = new WildCards<>();
}
}
Here we see there is no class exist ,
that implement interfaces B and C.
Hence "Wild Card" is must here.
import java.util.ArrayList;
import java.util.List;
public class WildCards<T extends List<?>> {
public static void main(String[] args) {
WildCards<ArrayList<Integer>> a = new WildCards<>();
WildCards<Float>> b = new WildCards<>();
WildCards<ArrayList<Double>> c = new WildCards<>();
WildCards<ArrayList<Comparable<Number>>> c1 = new WildCards<>();
// .....etc.
}
}
Here we see as List<?> is unbounded it take Integer,
Float, Double , Comparable etc. any types as its bound while
creating object.
import java.util.ArrayList;
import java.util.List;
public class WildCards<T extends List<?>> {
public static void main(String[] args) {
WildCards<ArrayList<Integer>> a = new WildCards<>();
WildCards<Float>> b = new WildCards<>();
WildCards<ArrayList<Double>> c = new WildCards<>();
WildCards<ArrayList<Comparable<Number>>> c1 = new WildCards<>();
// .....etc.
}
}
Here we see as List<?> is unbounded it take Integer,
Float, Double , Comparable etc. any types as its bound while
creating object.
import java.util.Set;
import java.util.List;
public class WildCards<T extends List<Number> & Set<Number>> {
public static void main(String[] args) {
WildCards<?> a = new WildCards<>();
WildCards<?> b = new WildCards<>();
WildCards<?> c = new WildCards<>();
// .....etc.
}
}
Here there is no class no class that implements,
both interface of List and Set. Hence we need to
pass "UnBound WildCard" as Type as shown above to
create Object.
import java.util.Set;
import java.util.List;
public class WildCards<T extends List<Number> & Set<Number>> {
public static void main(String[] args) {
WildCards<?> a = new WildCards<>();
WildCards<?> b = new WildCards<>();
WildCards<?> c = new WildCards<>();
// .....etc.
}
}
Here there is no class no class that implements,
both interface of List and Set. Hence we need to
pass "UnBound WildCard" as Type as shown above to
create Object.
3. When a class and interface is used in Multiple Upper Bound and no class exist that inherit both the class and interface.
import java.util.Set;
import java.util.ArrayList;
public class WildCards<T extends ArrayList<Number> & Set<Number>> {
public static void main(String[] args) {
WildCards<?> a = new WildCards<>();
WildCards<?> b = new WildCards<>();
WildCards<?> c = new WildCards<>();
// .....etc.
}
}
Here there are no class exist that extends ArrayList and implements Set
interface hence UnBounded Wild Card is used.
import java.util.Set;
import java.util.ArrayList;
public class WildCards<T extends ArrayList<Number> & Set<Number>> {
public static void main(String[] args) {
WildCards<?> a = new WildCards<>();
WildCards<?> b = new WildCards<>();
WildCards<?> c = new WildCards<>();
// .....etc.
}
}
Here there are no class exist that extends ArrayList and implements Set
interface hence UnBounded Wild Card is used.
- 1.Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
- Java compiler creates a synthetic method known as Bridge Method when compiling a class or interface that extends a parameterized class or interface as part of type erassure.
- Heap Pollution- Eg -1
- Heap Pollution- Eg -2
- 1.Cannot Instantiate Generic Types with Primitive Types
- 2.Cannot Create Instances of Type Parameters
- 3.Cannot Declare Static Fields Whose Types are Type Parameters or Restrictions of Static Members
- 4.Cannot Use Casts or instanceof with Parameterized Types
- 5.Cannot Create Arrays of Parameterized Types
- 6. Cannot Create, Catch, or Throw Objects of Parameterized Types
- 7. A class cannot have two overloaded methods that will have the same signature after type erasure.
- 1. Type Capture in Java Generics
- 2. Wild Card Capture in Java Generics and Helper Method
- 3. Raw Types And Sub Types
- 3. Generic Anonymous Class
Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:
Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.
class Node<T> {
T data;
Node<T> next;
public Node(T data) {
this.data = data;
this.next = null;
}
public static void main(String[] args) {
Node<String> n1 = new Node<String>("Hello");
Node<String> n2 = new Node<String>("World");
n1.next = n2;
System.out.println(n1.data);
System.out.println(n1.next.data);
}
}
class Node<T> {
T data;
Node<T> next;
public Node(T data) {
this.data = data;
this.next = null;
}
public static void main(String[] args) {
Node<String> n1 = new Node<String>("Hello");
Node<String> n2 = new Node<String>("World");
n1.next = n2;
System.out.println(n1.data);
System.out.println(n1.next.data);
}
}
class GenericBase {
public <T> T get(T t) {
return t;
}
public static void main(String[] args) {
GenericBase gb = new GenericBase();
System.out.println(gb.get(10));
}
}
class GenericBase {
public <T> T get(T t) {
return t;
}
public static void main(String[] args) {
GenericBase gb = new GenericBase();
System.out.println(gb.get(10));
}
}
Note here `T` replaces with Integer by compiler during Compilation and it erase the type 'T' with Integer . As above class is not generic , hence we have to put a cast < T > before starting the method. The type erasure process is shown below :
class Shape {...}
class Circle extends Shape {...}
class Rectangle extends Shape {...}
class Program {
public static <T extends Shape> void draw(T shape) {
System.out.println("Drawing a " + shape.getClass().getSimpleName());
}
public static void main(String[] args) {
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
draw(circle);
draw(rectangle);
}
}
class Shape {...}
class Circle extends Shape {...}
class Rectangle extends Shape {...}
class Program {
public static <T extends Shape> void draw(T shape) {
System.out.println("Drawing a " + shape.getClass().getSimpleName());
}
public static void main(String[] args) {
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
draw(circle);
draw(rectangle);
}
}
class Gener<T>{
T obj;
Gener(T o){
obj = o;
}
T getObj(){
return obj;
}
}
class Gener2 extends Gener<String>{
Gener2(String o){
super(o);
}
String getObj(){
System.out.println("Gener2's getObj()");
return obj;
}
}
class BridgeDemo {
public static void main(String args[]){
Gener2 strOb2 = new Gener2("Generics Test");
String str = strOb2.getObj();
System.out.println(str);
}
}
class Gener<T>{
T obj;
Gener(T o){
obj = o;
}
T getObj(){
return obj;
}
}
class Gener2 extends Gener<String>{
Gener2(String o){
super(o);
}
String getObj(){
System.out.println("Gener2's getObj()");
return obj;
}
}
class BridgeDemo {
public static void main(String args[]){
Gener2 strOb2 = new Gener2("Generics Test");
String str = strOb2.getObj();
System.out.println(str);
}
}
class Gener2 extends Gener<java.lang.String>{
//Type Erasure
Gener2(java.lang.String){
super(o)
}
//Bridge Method[Java Preserves It]
java.lang.Object getObj(){
return (java.lang.String) ob;
}
//Type Erasure
java.lang.String getObj(){
return (java.lang.String) ob;
}
}
class Gener2 extends Gener<java.lang.String>{
//Type Erasure
Gener2(java.lang.String){
super(o)
}
//Bridge Method[Java Preserves It]
java.lang.Object getObj(){
return (java.lang.String) ob;
}
//Type Erasure
java.lang.String getObj(){
return (java.lang.String) ob;
}
}
Here after Type Erasure , "java.lang.Object getObj();" is acceptable method . Here what happens is that "Due to Type Erasure , The Perfectly Acceptable form is : Object getob{...}, To handle this problem, the compiler generates a bridge method [Here is java.lang.Object getObj()] with the preceding signature that calls the String version "java.lang.String getObj()".The Bridge Method Is Used To Preserve The Type Safety Of Generic Types And The Polymorphism Of Generic Types After Type Erasure."
class Node1<T> {
T data;
Node1(T data) {
this.data = data;
}
void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
class MyNode extends Node1<Integer> {
MyNode(Integer data) {
super(data);
}
void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
class BridgeMethods {
public static void main(String[] args) {
Node1<Integer> node = new MyNode(5);
node.setData(4);
}
}
class Node1<T> {
T data;
Node1(T data) {
this.data = data;
}
void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
class MyNode extends Node1<Integer> {
MyNode(Integer data) {
super(data);
}
void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
class BridgeMethods {
public static void main(String[] args) {
Node1<Integer> node = new MyNode(5);
node.setData(4);
}
}
//Replacing Type with Object Class
class Node1 {
Object data;
Node1(Object data) {
this.data = data;
}
void setData(Object data) {
System.out.println("Node.setData");
this.data = data;
}
}
class MyNode extends Node1<Integer> {
MyNode(java.lang.Integer data) {
super(data);
}
//Bridge Method[Preserved]
void setData(java.lang.Object data) {
setData((java.lang.Integer)data);
}
//Type Erasure
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
//Replacing Type with Object Class
class Node1 {
Object data;
Node1(Object data) {
this.data = data;
}
void setData(Object data) {
System.out.println("Node.setData");
this.data = data;
}
}
class MyNode extends Node1<Integer> {
MyNode(java.lang.Integer data) {
super(data);
}
//Bridge Method[Preserved]
void setData(java.lang.Object data) {
setData((java.lang.Integer)data);
}
//Type Erasure
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
A Reifiable type is a type whose type information is fully available at runtime. Non-reifiable types are types where information has been removed at compile-time by type erasure — invocations of generic types that are not defined as unbounded wildcards. A non-reifiable type does not have all of its information available at runtime. Reifiable and Non-Reifiable types are already discussed earlier.
Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that parameterized type. This situation occurs if the program performed some operation that gives rise to an unchecked warning at compile-time. An unchecked warning is generated if, either at compile-time (within the limits of the compile-time type checking rules) or at runtime, the correctness of an operation involving a parameterized type (for example, a cast or method call) cannot be verified. For example, heap pollution occurs when mixing raw types and parameterized types, or when performing unchecked casts.
import java.util.ArrayList;
import java.util.List;
public class HeapPollution {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
integerList.add(42);
stringList = (List<String>) (List) integerList;
System.out.println(stringList.get(0));
}
}
import java.util.ArrayList;
import java.util.List;
public class HeapPollution {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
integerList.add(42);
stringList = (List<String>) (List) integerList;
System.out.println(stringList.get(0));
}
}
In the above code, a List named stringList and a List named integerList are created. Then, the integerList is added to stringList, but to make the assignment work, the integerList is cast to a raw type List and then cast to List.This is heap pollution because the stringList was intended to hold only String objects, but now it contains Integer objects as well. This can lead to unexpected behavior and errors when the stringList is accessed and the code expects to find only String objects in it.
// Heap pollution occurs when adding integerList to stringList
// because stringList is expected to hold only String objects
stringList = (List<String>) (List) integerList; // This causes heap pollution
// Heap pollution occurs when adding integerList to stringList
// because stringList is expected to hold only String objects
stringList = (List<String>) (List) integerList; // This causes heap pollution
To understand handling Heap Pollution, we have to understand : VarArgs of Java
import java.util.ArrayList;
import java.util.List;
public class HeapPollution {
public static void merge(List<String>... stringList) {
Object[] arr = stringList;
List<Integer> temp = new ArrayList<Integer>();
temp.add(420);
arr[0] = temp;
String firstEle = stringList[0].get(0);//ClassCastException
System.out.println(firstEle);
}
public static void main(String args[]) {
List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<String> list3 = new ArrayList<>();
list1.add("My Name");
list2.add("is");
list3.add("Avinandan");
merge(list1, list2, list3);
}
}
import java.util.ArrayList;
import java.util.List;
public class HeapPollution {
public static void merge(List<String>... stringList) {
Object[] arr = stringList;
List<Integer> temp = new ArrayList<Integer>();
temp.add(420);
arr[0] = temp;
String firstEle = stringList[0].get(0);//ClassCastException
System.out.println(firstEle);
}
public static void main(String args[]) {
List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<String> list3 = new ArrayList<>();
list1.add("My Name");
list2.add("is");
list3.add("Avinandan");
merge(list1, list2, list3);
}
}
Here , In the above program it throws warning Type safety: Potential heap pollution via varargs parameter stringList at List < String > ... stringList and stringList[0].get(0) tries to access the first element of the first list in the input using the get method, but this results in a ClassCastException because the first list has been replaced with an ArrayList and no longer contains strings i.e. [ ArrayList < String > ] during runtime .Hence after compilation firstEle becomes Integer type and change occurs during runtime causing ClassCastException.
Solution: To remove Warning we put the annotation @SafeVarargs and as the change occurs during runtime , we just change the type of "firstEle" to java.lang.Object to avoid "Class Cast Exception" as shown Below:
import java.util.ArrayList;
import java.util.List;
class Example{
public static <E> void rtti(List<E> list) {
//Illegal use of instanceof
------------------------------
if (list instanceof ArrayList<Integer>) {
System.out.println("ArrayList<Integer>");
}
}
}
import java.util.ArrayList;
import java.util.List;
class Example{
public static <E> void rtti(List<E> list) {
//Illegal use of instanceof
------------------------------
if (list instanceof ArrayList<Integer>) {
System.out.println("ArrayList<Integer>");
}
}
}
As Type E can be ArrayList < Integer > , ArrayList < String > etc. hence making E specific for ArrayList will throw compile-time error. To make it run successfully we must use "?" unbound as parameter:
import java.util.ArrayList;
import java.util.List;
class Example{
public static void rtti(List<?> list) {
if (list instanceof ArrayList<?>) {
System.out.println(list.getClass());
}
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
rtti(list);
}
}
import java.util.ArrayList;
import java.util.List;
class Example{
public static void rtti(List<?> list) {
if (list instanceof ArrayList<?>) {
System.out.println(list.getClass());
}
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
rtti(list);
}
}
import java.util.ArrayList;
import java.util.List;
class Example{
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
//Invalid Cast
List<Number> ln = (List<Number>) list;
}
}
import java.util.ArrayList;
import java.util.List;
class Example{
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
//Invalid Cast
List<Number> ln = (List<Number>) list;
}
}
Cannot cast ' java.util.List < java.lang.Integer > ' to ' java.util.List < java.lang.Number > ' as it cannot be converted. The valid cast would be:
import java.util.ArrayList;
import java.util.List;
class Example{
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
List<Integer> ln = (List<Integer>) list;
ln.add(1);
System.out.println(ln);
}
}
import java.util.ArrayList;
import java.util.List;
class Example{
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
List<Integer> ln = (List<Integer>) list;
ln.add(1);
System.out.println(ln);
}
}
import java.util.ArrayList;
import java.util.List;
class Example{
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
List<Integer> ln = (ArrayList<Integer>) list;
ln.add(1);
System.out.println(ln);
}
}
import java.util.ArrayList;
import java.util.List;
class Example{
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
List<Integer> ln = (ArrayList<Integer>) list;
ln.add(1);
System.out.println(ln);
}
}
Eg -1
import java.util.ArrayList;
class Example{
public static void main(String[] args) {
//Cannot Create Arrays of Parameterized Types
ArrayList<String>[] arrayOfLists = new ArrayList<String>[10];
}
}
Eg -1
import java.util.ArrayList;
class Example{
public static void main(String[] args) {
//Cannot Create Arrays of Parameterized Types
ArrayList<String>[] arrayOfLists = new ArrayList<String>[10];
}
}
Eg -2
class Example <T>{
public static void main(String[] args){
//Cannot Create Arrays of Parameterized Types
Example<Integer>[] e = new Example<Integer>[10];
}
}
Eg -2
class Example <T>{
public static void main(String[] args){
//Cannot Create Arrays of Parameterized Types
Example<Integer>[] e = new Example<Integer>[10];
}
}
Eg -3
class Example <T>{
public static void main(String[] args){
//Cannot Create Arrays of Parameterized Types
Example<Integer>[] e = new Example<Integer>[10];
}
}
Eg -3
class Example <T>{
public static void main(String[] args){
//Cannot Create Arrays of Parameterized Types
Example<Integer>[] e = new Example<Integer>[10];
}
}
Eg -4
class Example <T>{
T vals[];
public void arr(){
//Cannot create a generic array of T
//Also,Cannot be instantiated
vals = new T[10];
}
}
Eg -4
class Example <T>{
T vals[];
public void arr(){
//Cannot create a generic array of T
//Also,Cannot be instantiated
vals = new T[10];
}
}
Reason: The reason is that there is no way for the compiler to know what type of array to actually create .
Solution Eg-1
class Example <T>{
public void print(){
System.out.println("Example");
}
public static void main(String[] args) {
Example<?>[] e = new Example<?>[10];
e[0] = new Example<String>();
e[1] = new Example<Integer>();
e[2] = new Example<Double>();
e[0].print();
e[1].print();
e[2].print();
}
}
Solution Eg-1
class Example <T>{
public void print(){
System.out.println("Example");
}
public static void main(String[] args) {
Example<?>[] e = new Example<?>[10];
e[0] = new Example<String>();
e[1] = new Example<Integer>();
e[2] = new Example<Double>();
e[0].print();
e[1].print();
e[2].print();
}
}
Solution Eg-2
class Example<T > {
T vals[];
Example( T[] nums) {
vals = nums;
}
}
public static void main(String[]args){
Integer n[] = {1,2,3,4,5};
Example<Integer> iob = new Example<Integer>( n);
}
Solution Eg-2
class Example<T > {
T vals[];
Example( T[] nums) {
vals = nums;
}
}
public static void main(String[]args){
Integer n[] = {1,2,3,4,5};
Example<Integer> iob = new Example<Integer>( n);
}
import java.util.ArrayList;
class Example {
public void print(){
System.out.println("Example");
}
public static void main(String[]args){
ArrayList<? >[] al1 = new ArrayList<? >[10];
al1[0] = new ArrayList<String>();
//Compiler doesnot recognize the type .
al1[0].add("Hello"); //Error
}
}
import java.util.ArrayList;
class Example {
public void print(){
System.out.println("Example");
}
public static void main(String[]args){
ArrayList<? >[] al1 = new ArrayList<? >[10];
al1[0] = new ArrayList<String>();
//Compiler doesnot recognize the type .
al1[0].add("Hello"); //Error
}
}
import java.util.ArrayList;
class Example {
public void print(){
System.out.println("Example");
}
public static void main(String[]args){
ArrayList<? extends String >[] al1 =
(ArrayList<? extends String>[]) new ArrayList<? >[10];
al1[0] = new ArrayList<String>();
//Compiler doesnot recognize the type .
al1[0].add("Hello");//Error
}
}
import java.util.ArrayList;
class Example {
public void print(){
System.out.println("Example");
}
public static void main(String[]args){
ArrayList<? extends String >[] al1 =
(ArrayList<? extends String>[]) new ArrayList<? >[10];
al1[0] = new ArrayList<String>();
//Compiler doesnot recognize the type .
al1[0].add("Hello");//Error
}
}
Solution Eg-3
import java.util.ArrayList;
class Example {
public void print(){
System.out.println("Example");
}
public static void main(String[]args){
ArrayList<? super String>[] al1 =
(ArrayList<? super String>[]) new ArrayList<?>[10];
al1[0] = new ArrayList<String>();
al1[0].add("Hello"); //OK
System.out.println(al1[0]);
}
}
Solution Eg-3
import java.util.ArrayList;
class Example {
public void print(){
System.out.println("Example");
}
public static void main(String[]args){
ArrayList<? super String>[] al1 =
(ArrayList<? super String>[]) new ArrayList<?>[10];
al1[0] = new ArrayList<String>();
al1[0].add("Hello"); //OK
System.out.println(al1[0]);
}
}
import java.util.ArrayList;
class Example<T extends Exception> {
public T get(ArrayList<T> t) {
try {
System.out.println("try");
return t.get(0);
}
catch (T e) //Error
{
return (T) e;
}
}
}
import java.util.ArrayList;
class Example<T extends Exception> {
public T get(ArrayList<T> t) {
try {
System.out.println("try");
return t.get(0);
}
catch (T e) //Error
{
return (T) e;
}
}
}
import java.util.ArrayList;
class Example<T extends Exception> {
public T get(ArrayList<T> t) throws T {
return t.get(0);
}
public static void main(String[] args) {
Example<Exception> e = new Example<>();
ArrayList<Exception> al = new ArrayList<>();
al.add(new Exception("Exception Thrown"));
try {
System.out.println(e.get(al));
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
import java.util.ArrayList;
class Example<T extends Exception> {
public T get(ArrayList<T> t) throws T {
return t.get(0);
}
public static void main(String[] args) {
Example<Exception> e = new Example<>();
ArrayList<Exception> al = new ArrayList<>();
al.add(new Exception("Exception Thrown"));
try {
System.out.println(e.get(al));
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
In Java Generics, capture is the process of taking a type variable in a generic method or class, and introducing a new type parameter that captures the specific type that is inferred for that type variable in a given context.
In other words, when the Java compiler encounters a generic method or class with a type variable, it needs to determine the actual type that will be used for that variable when the method or class is called. This process is called type inference. In some cases, the type inference algorithm can be ambiguous or unpredictable, which can lead to compile errors or unexpected runtime behavior.
To resolve this issue, the compiler creates a new type parameter that "captures" the actual type that is inferred for the type variable in the current context. This new type parameter is called a capture, and it ensures that the generic method or class can work correctly with the specific type that is inferred for the type variable.
Such as we have :
class Example{
public static <T extends Comparable<T>> T[] max(T[] a, T[] b) {
for (int i = 0; i < a.length; i++) {
if (a[i].compareTo(b[i]) > 0) {
return a;
}
}
return b;
}
public static void main(String[] args) {
Integer[] arr1 = {1, 2, 3};
Integer[] arr2 = {4, 5, 6};
Integer[] maxArr = max(arr1, arr2);
for (Integer i : maxArr) {
System.out.println(i);
}
}
}
Now while compilation ,
Compiler can create its own "Capture" the above method as:
public static <T extends Comparable<T>> T[] maxHelper (T[] a, T[] b) {
for (int i = 0; i < a.length; i++) {
if (a[i].compareTo(b[i]) > 0) {
return a;
}
}
return b;
}
i.e.
And check that the type provided by the User is correct and then compile ,
And it is not visible to Programmer as Compiler creates it internally i.e.
---------
class Example{
public static <T extends Comparable<T>> T[] max(T[] a, T[] b) {
for (int i = 0; i < a.length; i++) {
if (a[i].compareTo(b[i]) > 0) {
return a;
}
}
return b;
}
public static void main(String[] args) {
Integer[] arr1 = {1, 2, 3};
Integer[] arr2 = {4, 5, 6};
Integer[] maxArr = max(arr1, arr2);
for (Integer i : maxArr) {
System.out.println(i);
}
}
//During Compilation(Compiler creates an Capture) for type check
public static <T extends Comparable<T>> T[] maxHelper (T[] a, T[] b) {
for (int i = 0; i < a.length; i++) {
if (a[i].compareTo(b[i]) > 0) {
return a;
}
}
return b;
}
//Known to compiler Only, not visible to Programmer.
}
Such as we have :
class Example{
public static <T extends Comparable<T>> T[] max(T[] a, T[] b) {
for (int i = 0; i < a.length; i++) {
if (a[i].compareTo(b[i]) > 0) {
return a;
}
}
return b;
}
public static void main(String[] args) {
Integer[] arr1 = {1, 2, 3};
Integer[] arr2 = {4, 5, 6};
Integer[] maxArr = max(arr1, arr2);
for (Integer i : maxArr) {
System.out.println(i);
}
}
}
Now while compilation ,
Compiler can create its own "Capture" the above method as:
public static <T extends Comparable<T>> T[] maxHelper (T[] a, T[] b) {
for (int i = 0; i < a.length; i++) {
if (a[i].compareTo(b[i]) > 0) {
return a;
}
}
return b;
}
i.e.
And check that the type provided by the User is correct and then compile ,
And it is not visible to Programmer as Compiler creates it internally i.e.
---------
class Example{
public static <T extends Comparable<T>> T[] max(T[] a, T[] b) {
for (int i = 0; i < a.length; i++) {
if (a[i].compareTo(b[i]) > 0) {
return a;
}
}
return b;
}
public static void main(String[] args) {
Integer[] arr1 = {1, 2, 3};
Integer[] arr2 = {4, 5, 6};
Integer[] maxArr = max(arr1, arr2);
for (Integer i : maxArr) {
System.out.println(i);
}
}
//During Compilation(Compiler creates an Capture) for type check
public static <T extends Comparable<T>> T[] maxHelper (T[] a, T[] b) {
for (int i = 0; i < a.length; i++) {
if (a[i].compareTo(b[i]) > 0) {
return a;
}
}
return b;
}
//Known to compiler Only, not visible to Programmer.
}
Wild Card Capture is a mechanism in Java Generics that allows a wildcard expression to be captured by a type parameter, in a similar way to how a type variable can be captured by a type parameter using capture conversion.
In Java Generics, a wildcard expression is a type argument that uses the wildcard character ("?") to represent an unknown type. Wildcards are commonly used in generic methods and classes to provide flexibility and enable more general-purpose code.
However, when a wildcard is used in a context where a type parameter is expected, the Java compiler needs to perform capture conversion to infer the actual type that should be used for the wildcard. This process can be complex and may lead to unexpected behavior if not handled correctly.
To avoid this issue, the Java compiler can create a new type parameter that "captures" the wildcard expression, similar to how capture conversion works for type variables. This new type parameter can then be used in place of the wildcard expression, ensuring that the generic method or class works correctly with the specific type that is inferred for the wildcard.
Wild Card Capture is an internal mechanism used by the Java compiler and is not directly visible to the programmer.
Like :
public class Example {
public static <T>void printList(List<T> list) {
for (T o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3);
printList(numbers);
}
-----------------------------
//Compiler create a Capture
----------------------------------
public static void printList(List<Integer> list) {
for (Integer o : list) {
System.out.println(o);
}
}
----------------------------------
//Only known to Compiler
--------------------------------
Similarly:
public class Example {
public static <T>void printList(List<?> list) {
for (Object o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3);
printList(numbers);
}
}
-----------------------------
//Compiler create a Capture
------------------------------
public static void printList(List<Integer> list) {
for (Integer o : list) {
System.out.println(o);
}
}
-----------------------------
//Only known to Compiler
-----------------------------
**************************************
**************************************
*Also for Multiple Upper Bound Type:*
**************************************
**************************************
public class Example {
public static <T>void printList(List<? extends Number> list) {
for (Object o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3);
printList(numbers);
}
}
---------------------------
//Compiler create a Capture
--------------------------
public static void printList(List<Integer extends Number> list) {
for (Integer o : list) {
System.out.println(o);
}
}
---------------------------
//Only known to Compiler
---------------------------------
**************************************
**************************************
* Also for Lower Bound Type: *
**************************************
**************************************
public class Example {
public static <T>void printList(List<? super Number> list) {
for (Object o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
List<Number> numbers = Arrays.asList(1, 2, 3);
printList(numbers);
}
}
--------------------------
//Compiler create a Capture
------------------------------
public static void printList(List<Number super Number> list) {
for (Number o : list) {
System.out.println(o);
}
}
---------------------------
//Only known to Compiler
------------------------------
Like :
public class Example {
public static <T>void printList(List<T> list) {
for (T o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3);
printList(numbers);
}
-----------------------------
//Compiler create a Capture
----------------------------------
public static void printList(List<Integer> list) {
for (Integer o : list) {
System.out.println(o);
}
}
----------------------------------
//Only known to Compiler
--------------------------------
Similarly:
public class Example {
public static <T>void printList(List<?> list) {
for (Object o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3);
printList(numbers);
}
}
-----------------------------
//Compiler create a Capture
------------------------------
public static void printList(List<Integer> list) {
for (Integer o : list) {
System.out.println(o);
}
}
-----------------------------
//Only known to Compiler
-----------------------------
**************************************
**************************************
*Also for Multiple Upper Bound Type:*
**************************************
**************************************
public class Example {
public static <T>void printList(List<? extends Number> list) {
for (Object o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3);
printList(numbers);
}
}
---------------------------
//Compiler create a Capture
--------------------------
public static void printList(List<Integer extends Number> list) {
for (Integer o : list) {
System.out.println(o);
}
}
---------------------------
//Only known to Compiler
---------------------------------
**************************************
**************************************
* Also for Lower Bound Type: *
**************************************
**************************************
public class Example {
public static <T>void printList(List<? super Number> list) {
for (Object o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
List<Number> numbers = Arrays.asList(1, 2, 3);
printList(numbers);
}
}
--------------------------
//Compiler create a Capture
------------------------------
public static void printList(List<Number super Number> list) {
for (Number o : list) {
System.out.println(o);
}
}
---------------------------
//Only known to Compiler
------------------------------
Raw Types: Raw types in Java Generics are a way to use a generic class or method without specifying its type parameters. Raw types are created by omitting the type parameter(s) from the generic class or method name.
import java.util.ArrayList;
import java.util.List;
public class Example <T extends List<Number>>{
public static <T>void printList(List<? super Number> list) {
for (Object o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
Example eg = new Example();
List<Number> li = new ArrayList<>();
li.add(1);
li.add(2);
eg.printList(li);
}
}
import java.util.ArrayList;
import java.util.List;
public class Example <T extends List<Number>>{
public static <T>void printList(List<? super Number> list) {
for (Object o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
Example eg = new Example();
List<Number> li = new ArrayList<>();
li.add(1);
li.add(2);
eg.printList(li);
}
}
Sub Types: 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.
import java.util.ArrayList;
import java.util.List;
interface b2<T> extends List<T> { }
public class Example3<T extends b2<? extends Number>> {
T get(T t) {
t.forEach(System.out::println);
return t;
}
public static void main(String[] args) {
Example3<b2<Number>> e = new Example3<>();
Example3<b2<Integer>> e1 = new Example3<>();
List<Number> al = new ArrayList<>();
al.add(1);
al.add(2);
al.add(3);
System.out.println(e.get((b2<Number>) al));
List<Integer> al1 = new ArrayList<>();
al1.add(1);
al1.add(2);
al1.add(3);
System.out.println(e1.get((b2<Integer>) al1));
}
}
import java.util.ArrayList;
import java.util.List;
interface b2<T> extends List<T> { }
public class Example3<T extends b2<? extends Number>> {
T get(T t) {
t.forEach(System.out::println);
return t;
}
public static void main(String[] args) {
Example3<b2<Number>> e = new Example3<>();
Example3<b2<Integer>> e1 = new Example3<>();
List<Number> al = new ArrayList<>();
al.add(1);
al.add(2);
al.add(3);
System.out.println(e.get((b2<Number>) al));
List<Integer> al1 = new ArrayList<>();
al1.add(1);
al1.add(2);
al1.add(3);
System.out.println(e1.get((b2<Integer>) al1));
}
}
interface b2<T> extends List<T> { }
Then:
class Example<T extends b2<? extends Number>>{
public static void main(String[] args) {
Example3<b2<Number>> e = new Example3<>();
Example3<b2<Integer>> e1 = new Example3<>();
Example3<b2<FLoat>> e1 = new Example3<>();
Example3<b2<Double>> e1 = new Example3<>();
}
}
i.e.,
List<Number> --> b2<Number>
List<Number> --> b2<Integer>
List<Number> --> b2<FLoat>
List<Number> --> b2<Double>
interface b2<T> extends List<T> { }
Then:
class Example<T extends b2<? extends Number>>{
public static void main(String[] args) {
Example3<b2<Number>> e = new Example3<>();
Example3<b2<Integer>> e1 = new Example3<>();
Example3<b2<FLoat>> e1 = new Example3<>();
Example3<b2<Double>> e1 = new Example3<>();
}
}
i.e.,
List<Number> --> b2<Number>
List<Number> --> b2<Integer>
List<Number> --> b2<FLoat>
List<Number> --> b2<Double>
Generic Anonymous Class: Like Anonymous CLass as those classes are nested class which have no name , hence Anonymous.Whatever the class returns stored in a variable created through that Class i.e. object of that class.As they are nested inside a class , also known as Anonymous Inner Class .As they can be Parameterized, hence Generic Anonymous Class.And they override the super class/abstract class/Interface's method, while inheriting it.
++++++++
+Eg: 1 +
++++++++
interface A<T> {
T get(T t);
}
public class Example {
//Anynomous Generic Class
A<String> a = new A<String>() {
@Override
public String get(String s) {
System.out.println(s);
return s;
}
};
public static void main(String[] args) {
Example e = new Example();
e.a.get("Hello");
}
}
++++++++
+Eg: 1 +
++++++++
interface A<T> {
T get(T t);
}
public class Example {
//Anynomous Generic Class
A<String> a = new A<String>() {
@Override
public String get(String s) {
System.out.println(s);
return s;
}
};
public static void main(String[] args) {
Example e = new Example();
e.a.get("Hello");
}
}
++++++++
+Eg: 2 +
++++++++
abstract class A2<T extends Number>{
public abstract T m(T a);
}
class Example3{
public static void main(String[] args) {
A2<Number> a = new A2< Number>() {
@Override
public Number m(Number a) {
return a;
}
};
System.out.println(a.m(1));
System.out.println(a.m(1.0));
System.out.println(a.m(1.0f));
A2<Integer> a1 = new A2< Integer>() {
@Override
public Integer m(Integer a) {
return a;
}
};
System.out.println(a1.m(1));
System.out.println(a1.m(2));
}
}
++++++++
+Eg: 2 +
++++++++
abstract class A2<T extends Number>{
public abstract T m(T a);
}
class Example3{
public static void main(String[] args) {
A2<Number> a = new A2< Number>() {
@Override
public Number m(Number a) {
return a;
}
};
System.out.println(a.m(1));
System.out.println(a.m(1.0));
System.out.println(a.m(1.0f));
A2<Integer> a1 = new A2< Integer>() {
@Override
public Integer m(Integer a) {
return a;
}
};
System.out.println(a1.m(1));
System.out.println(a1.m(2));
}
}
abstract class A2<T extends Number>{
public abstract T m(T a);
}
class Example3{
public static void main(String[] args) {
A2<Number> a = new A2< Number>() {
@Override
public Number m(Number a) {
return a;
}
};
A2<Integer> a1 = new A2< Integer>() {
@Override
public Integer m(Integer a) {
return a;
}
};
A2<Double> a2 = new A2< Double>() {
@Override
public Double m(Double a) {
return a;
}
};
A2<Float> a3 = new A2< Float>() {
@Override
public Float m(Float a) {
return a;
}
};
//....etc.
}
}
abstract class A2<T extends Number>{
public abstract T m(T a);
}
class Example3{
public static void main(String[] args) {
A2<Number> a = new A2< Number>() {
@Override
public Number m(Number a) {
return a;
}
};
A2<Integer> a1 = new A2< Integer>() {
@Override
public Integer m(Integer a) {
return a;
}
};
A2<Double> a2 = new A2< Double>() {
@Override
public Double m(Double a) {
return a;
}
};
A2<Float> a3 = new A2< Float>() {
@Override
public Float m(Float a) {
return a;
}
};
//....etc.
}
}
++++++++
+ Eg-3 +
++++++++
import java.util.ArrayList;
import java.util.List;
abstract class A2<T extends List<? extends Number>>{
public abstract T m(T a);
}
class Example3{
public static void main(String[] args) {
A2<List<Number>> a = new A2< List<Number>>() {
@Override
public List<Number> m(List<Number> a) {
return a;
}
};
List<Number> al = new ArrayList<>();
al.add(1);
al.add(2.0f);
al.add(3.33D);
System.out.println(a.m(al));
A2<List<Integer>> a1 = new A2< List<Integer>>() {
@Override
public List<Integer> m(List<Integer> a) {
return a;
}
};
List<Integer> al1 = new ArrayList<>();
al1.add(1);
al1.add(2);
al1.add(3);
System.out.println(a1.m(al1));
//Same for Double, Float,
// Long, Short, Byte,
//Which are extended by Number
}
}
++++++++
+ Eg-3 +
++++++++
import java.util.ArrayList;
import java.util.List;
abstract class A2<T extends List<? extends Number>>{
public abstract T m(T a);
}
class Example3{
public static void main(String[] args) {
A2<List<Number>> a = new A2< List<Number>>() {
@Override
public List<Number> m(List<Number> a) {
return a;
}
};
List<Number> al = new ArrayList<>();
al.add(1);
al.add(2.0f);
al.add(3.33D);
System.out.println(a.m(al));
A2<List<Integer>> a1 = new A2< List<Integer>>() {
@Override
public List<Integer> m(List<Integer> a) {
return a;
}
};
List<Integer> al1 = new ArrayList<>();
al1.add(1);
al1.add(2);
al1.add(3);
System.out.println(a1.m(al1));
//Same for Double, Float,
// Long, Short, Byte,
//Which are extended by Number
}
}
++++++++
+ Eg-4 +
++++++++
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
abstract class A2<T extends AbstractList<Number>&List<Number>>{
public abstract T m(T a);
}
class Example3{
public static void main(String[] args) {
A2<ArrayList<Number>> a = new A2< ArrayList<Number>>() {
@Override
public ArrayList<Number> m(ArrayList<Number> a) {
return a;
}
};
ArrayList<Number> al = new ArrayList<>();
al.add(1);
al.add(2.0f);
al.add(3.33D);
System.out.println(a.m(al));
}
}
++++++++
+ Eg-4 +
++++++++
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
abstract class A2<T extends AbstractList<Number>&List<Number>>{
public abstract T m(T a);
}
class Example3{
public static void main(String[] args) {
A2<ArrayList<Number>> a = new A2< ArrayList<Number>>() {
@Override
public ArrayList<Number> m(ArrayList<Number> a) {
return a;
}
};
ArrayList<Number> al = new ArrayList<>();
al.add(1);
al.add(2.0f);
al.add(3.33D);
System.out.println(a.m(al));
}
}
++++++++
+ Eg-5 +
++++++++
import java.util.ArrayList;
import java.util.List;
class A2<T extends List<? super Number>>{
public T m(T a){
return a;
};
}
class Example3{
public static void main(String[] args) {
A2<ArrayList<Number>> a = new A2< ArrayList<Number>>() {
@Override
public ArrayList<Number> m(ArrayList<Number> a) {
return a;
}
};
ArrayList<Number> al = new ArrayList<>();
al.add(1);
al.add(2.0f);
al.add(3.33D);
System.out.println(a.m(al));
A2<ArrayList<Object>> a1 = new A2< ArrayList<Object>>() {
@Override
public ArrayList<Object> m(ArrayList<Object> a) {
return a;
}
};
ArrayList<Object> al1 = new ArrayList<>();
al1.add(1);
al1.add(2.0f);
al1.add(3.33D);
System.out.println(a1.m(al1));
A2<ArrayList<Serializable>> a2 = new A2< ArrayList<Serializable>>() {
@Override
public ArrayList<Serializable> m(ArrayList<Serializable> a) {
return a;
}
};
ArrayList<Serializable> al2 = new ArrayList<>();
al2.add(1);
al2.add(2.0f);
al2.add(3.33D);
System.out.println(a2.m(al2));
//Object and Serializable are super types of Number class
}
}
++++++++
+ Eg-5 +
++++++++
import java.util.ArrayList;
import java.util.List;
class A2<T extends List<? super Number>>{
public T m(T a){
return a;
};
}
class Example3{
public static void main(String[] args) {
A2<ArrayList<Number>> a = new A2< ArrayList<Number>>() {
@Override
public ArrayList<Number> m(ArrayList<Number> a) {
return a;
}
};
ArrayList<Number> al = new ArrayList<>();
al.add(1);
al.add(2.0f);
al.add(3.33D);
System.out.println(a.m(al));
A2<ArrayList<Object>> a1 = new A2< ArrayList<Object>>() {
@Override
public ArrayList<Object> m(ArrayList<Object> a) {
return a;
}
};
ArrayList<Object> al1 = new ArrayList<>();
al1.add(1);
al1.add(2.0f);
al1.add(3.33D);
System.out.println(a1.m(al1));
A2<ArrayList<Serializable>> a2 = new A2< ArrayList<Serializable>>() {
@Override
public ArrayList<Serializable> m(ArrayList<Serializable> a) {
return a;
}
};
ArrayList<Serializable> al2 = new ArrayList<>();
al2.add(1);
al2.add(2.0f);
al2.add(3.33D);
System.out.println(a2.m(al2));
//Object and Serializable are super types of Number class
}
}
interface A<T> {
public T get(T a);
}
interface B<T> {
public T get(T b);
}
class C<T> implements A<T>, B<T> {
public T get(T c) {
return c;
}
}
class Example<T extends A<? extends Number> & B<? extends Number>>{
public static void main(String[] args) {
A<Number> a = new A<Number>(){
@Override
public Number get(Number a) {
return a;
}
};
System.out.println(a.get(1));
B<Integer> b = new B<Integer>(){
@Override
public Integer get(Integer b) {
return b;
}
};
System.out.println(b.get(2));
}
// Same for Double, Float,
// Long, Short, Byte,
// Which are extended by Number
}
import java.io.Serializable;
interface A1<T> {
public T get(T a);
}
interface B1<T> {
public T get(T b);
}
class C1<T> implements A1<T>, B1<T> {
public T get(T c) {
return c;
}
}
public class Example<T extends A1<? super Number> & B1<? super Number>> {
public static void main(String[] args) {
A1<Number> a = new A1<Number>(){
@Override
public Number get(Number a) {
return a;
}
};
System.out.println(a.get(1));
B1<Object> b = new B1<Object>(){
@Override
public Object get(Object b) {
return b;
}
};
System.out.println(b.get(2));
B1<Serializable> b2 = new B1<Serializable>() {
@Override
public Serializable get(Serializable b) {
return b;
}
};
System.out.println(b2.get(3));
}
}
class/ abstract class/Interface Eg <TYPE >{
Method();
}
class Ex{
Eg <type> e = Eg<type>{
@Ovveride
Method();
}
}
//type depends upon Type
class/ abstract class/Interface Eg <TYPE >{
Method();
}
class Ex{
Eg <type> e = Eg<type>{
@Ovveride
Method();
}
}
//type depends upon Type
***Until JDK 9 arrived , the diamond operator and generic class is possible only with normal classes , i.e. if we run any program with anonymous class and type parameter in JDK 7 it will throw error , hence Java developers extended the feature of the diamond operator in JDK 9 by allowing the diamond operator to be used with anonymous inner classes too.***
- 1) Nested Inner Class inside a Class
- 1. Nested Inner Class inside a Class- Eg(1)
- Using Upper Bound
- Using Upper Bound
- Using Multiple Upper Bound
- Using Lower Bound
- 2) Nested Inner Class in Local Method of a Class
- 1. Nested Inner Class in Local Method of a Class- Eg(1)
- Using Generic in Method
- Using Upper Bound in Method
- Using Multiple Upper Bound in Method
- Using Lower Bound in Method
- 2) Anonymous Inner Class
- 3) Static Nested Inner Class
Diamond operator was introduced in Java 7 .The main purpose of the diamond operator is to simplify the use of generics when creating an object. The diamond operator could not be used with Anonymous inner classes in JDK 7. In JDK 9, it can be used with the anonymous class as well to simplify code and improves readability. [As mentioned above]
OuterClass{
InnerNestedClass{
}
}
main(){
//Creation of Outer Class Object
OuterClass outerObj = new OuterClass();
//Creation of Inner Class Object
OuterClass.InnerNestedClass innerobj = outerObj. new InnerNestedClass();
}
OuterClass{
InnerNestedClass{
}
}
main(){
//Creation of Outer Class Object
OuterClass outerObj = new OuterClass();
//Creation of Inner Class Object
OuterClass.InnerNestedClass innerobj = outerObj. new InnerNestedClass();
}
OuterClass{
LocalMethod(){
InnerNestedClass{
}
}
//Creation of Inner Class Object
InnerNestedClass innerobj = new InnerNestedClass();
}
main(){
//Creation of Outer Class Object
OuterClass outerObj = new OuterClass();
//Calling the local Method
outerObj.LocalMethod();
}
OuterClass{
LocalMethod(){
InnerNestedClass{
}
}
//Creation of Inner Class Object
InnerNestedClass innerobj = new InnerNestedClass();
}
main(){
//Creation of Outer Class Object
OuterClass outerObj = new OuterClass();
//Calling the local Method
outerObj.LocalMethod();
}
++++++
Class
++++++
class A{
LocalMethod(){...}
}
class B{
Main(){
A a = new A(){
@override
LocalMethod(){...}
}
}
}
}
++++++
Abstract Class
++++++
abstract class AnonymousInner {
abstract LocalMethod();
}
class Inner{
Main(){
AnonymousInner anIn = new AnonymousInner(){
@Override
LocalMethod(){....}
}
}
}
++++++
Interface
++++++
interface A {
LocalInterfaceMethod();
}
class Eg implements A{
@Override
LocalInterfaceMethod(){...}
}
class InnerClass{
Main(){
A a = new A(){
@Override
LocalInterfaceMethod(){...};
}
}
}
++++++
Class
++++++
class A{
LocalMethod(){...}
}
class B{
Main(){
A a = new A(){
@override
LocalMethod(){...}
}
}
}
}
++++++
Abstract Class
++++++
abstract class AnonymousInner {
abstract LocalMethod();
}
class Inner{
Main(){
AnonymousInner anIn = new AnonymousInner(){
@Override
LocalMethod(){....}
}
}
}
++++++
Interface
++++++
interface A {
LocalInterfaceMethod();
}
class Eg implements A{
@Override
LocalInterfaceMethod(){...}
}
class InnerClass{
Main(){
A a = new A(){
@Override
LocalInterfaceMethod(){...};
}
}
}
Implementation of Lambda Expression, Functional Interface Method References AND ConstructorReference in Java Generics
- Simple Example of Implementation →Eg-1
- Implementation of Lambda and Using Upper Bound →Eg-2
- Implementation of Lambda and Using Upper Bound →Eg-3
- Implementation of Lambda and Using Multiple Upper Bound →Eg-4
- Implementation of Lambda and Using Upper Bound with WildCard →Eg-5
- Implementation of Lambda with Multiple Parameter and Using Multiple Upper Bound with WildCard →Eg-6
- Implementation of Lambda with Multiple Parameter and Using Multiple Upper Bound and Lower Bound with WildCard →Eg-7
- Passing Wild Card in creation of Object →Eg-8
- Passing Lambda Expression as Argument Using Generics→Eg-9
- Simple Implementation of ClassName::methodName in Generics→Eg-1
- Implementation of ClassName::methodName in UpperBound Class→Eg-2
- Implementation of ClassName::methodName in Multiple UpperBound Class→Eg-3
- Implementation of ClassName::methodName in Multiple UpperBound with WildCard Class→Eg-4
- Implementation of ClassName::methodName in Lower Bound with WildCard Class→Eg-5
- Simple Implementation of ClassName::methodName in Generics(2)→Eg-6
Rule:
Here is a rule , constructor reference depends upon the
Functional Interface and calls the single function in the interface,
the Type parameter of Interface should match with the,
Class's type parameter.
i.e.,
interface ConstRef1<T extends Number>
{
MyFunc1<T>func();
}
class MyFunc1<T extends Number>
{
MyFunc();
}
Hence, <T extends Number> should exist in both.
Rule:
Note: In wild card upper bound if class Type is,
extended to a class say Number then interface Type
should be those classes which extends the Type Class
(Sub Classes)or remain Same.
//Same
interface ConstRef1<T extends List < ? extends Number>>
{
MyFunc1<T>func();
}
class MyFunc1<T extends List < ? extends Number>>
{
MyFunc();
}
:OR:
//Using Sub Class in Interface
interface ConstRef1<T extends List <Integer>>
{
MyFunc1<T>func();
}
class MyFunc1<T extends List < ? extends Number>>
{
MyFunc();
}
Rule:
Note: In wild card upper bound if class Type is,
Super to a class say Number then interface Type
should be of Super classes or remain Same.
//Same
interface ConstRef1<T extends List < ? super Number>>
{
MyFunc1<T>func();
}
class MyFunc1<T extends List < ? super Number>>
{
MyFunc();
}
:OR:
//Using Sub Class in Interface
interface ConstRef1<T extends List <Serializable>
{
MyFunc1<T>func();
}
class MyFunc1<T extends List < ? extends Number>>
{
MyFunc();
}
Note: Object,Serializable are super Classes of Number
including Number itself.
- Hash Set in Java Generics →Eg-1
- Hash Set in Java Generics →Eg-2
- Hash Set in Java Generics →Eg-3(1)(Constructor)
- Hash Set in Java Generics →Eg-3(2)(Constructor)
- Hash Set in Java Generics →Eg-3(3)(Constructor)
- Hash Set in Java Generics →Eg-3(4)(Constructor)
- Hash Set in Java Generics →Eg-3(5)(Constructor)(User Input-Type1)
- Hash Set in Java Generics →Eg-3(6)(Constructor)(User Input-Type2)
- Hash Set in Java Generics →Eg-4(Function)
- Hash Set in Java Generics →Eg-4(1)(Function-User Input)
- Hash Set in Java Generics →Eg-5(Constructor and Function)
- Hash Set in Java Generics →Eg-5(1)(Constructor and Function)
- Simple Implementation of Map with Single Parameter
- Simple Implementation of Map with Two Different Parameter
- Using Constructor to have different Functionalities of Map
- Using Constructor to Generate sum of all Keys using Stream and Lambda Function.
- Using Constructor to put Array Values in Map using Java Generics.