eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Partner – LambdaTest – NPI EA (cat=Testing)
announcement - icon

Regression testing is an important step in the release process, to ensure that new code doesn't break the existing functionality. As the codebase evolves, we want to run these tests frequently to help catch any issues early on.

The best way to ensure these tests run frequently on an automated basis is, of course, to include them in the CI/CD pipeline. This way, the regression tests will execute automatically whenever we commit code to the repository.

In this tutorial, we'll see how to create regression tests using Selenium, and then include them in our pipeline using GitHub Actions:, to be run on the LambdaTest cloud grid:

>> How to Run Selenium Regression Tests With GitHub Actions

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Introduction

In this article, we’ll go through some example Java generics interview questions and answers.

Generics are a core concept in Java, first introduced in Java 5. Because of this, nearly all Java codebases will make use of them, almost guaranteeing that a developer will run into them at some point. This is why it’s essential to understand them correctly, and is why they are more than likely to be asked about during an interview process.

2. Questions

Q1. What Is a Generic Type Parameter?

Type is the name of a class or interface. As implied by the name, a generic type parameter is when a type can be used as a parameter in a class, method or interface declaration.

Let’s start with a simple example, one without generics, to demonstrate this:

public interface Consumer {
    public void consume(String parameter)
}

In this case, the method parameter type of the consume() method is String. It is not parameterized and not configurable.

Now let’s replace our String type with a generic type that we will call T. It is named like this by convention:

public interface Consumer<T> {
    public void consume(T parameter)
}

When we implement our consumer, we can provide the type that we want it to consume as an argument. This is a generic type parameter:

public class IntegerConsumer implements Consumer<Integer> {
    public void consume(Integer parameter)
}

In this case, now we can consume integers. We can swap out this type for whatever we require.

Q2. What Are Some Advantages of Using Generic Types?

One advantage of using generics is avoiding casts and provide type safety. This is particularly useful when working with collections. Let’s demonstrate this:

List list = new ArrayList();
list.add("foo");
Object o = list.get(0);
String foo = (String) o;

In our example, the element type in our list is unknown to the compiler. This means that the only thing that can be guaranteed is that it is an object. So when we retrieve our element, an Object is what we get back. As the authors of the code, we know it’s a String, but we have to cast our object to one to fix the problem explicitly. This produces a lot of noise and boilerplate.

Next, if we start to think about the room for manual error, the casting problem gets worse. What if we accidentally had an Integer in our list?

list.add(1)
Object o = list.get(0);
String foo = (String) o;

In this case, we would get a ClassCastException at runtime, as an Integer cannot be cast to String.

Now, let’s try repeating ourselves, this time using generics:

List<String> list = new ArrayList<>();
list.add("foo");
String o = list.get(0);    // No cast
Integer foo = list.get(0); // Compilation error

As we can see, by using generics we have a compile type check which prevents ClassCastExceptions and removes the need for casting.

The other advantage is to avoid code duplication. Without generics, we have to copy and paste the same code but for different types. With generics, we do not have to do this. We can even implement algorithms that apply to generic types.

Q3. What Is Type Erasure?

It’s important to realize that generic type information is only available to the compiler, not the JVM. In other words, type erasure means that generic type information is not available to the JVM at runtime, only compile time.

The reasoning behind major implementation choice is simple – preserving backward compatibility with older versions of Java. When a generic code is compiled into bytecode, it will be as if the generic type never existed. This means that the compilation will:

  1. Replace generic types with objects
  2. Replace bounded types (More on these in a later question) with the first bound class
  3. Insert the equivalent of casts when retrieving generic objects.

It’s important to understand type erasure. Otherwise, a developer might get confused and think they’d be able to get the type at runtime:

public foo(Consumer<T> consumer) {
   Type type = consumer.getGenericTypeParameter()
}

The above example is a pseudo code equivalent of what things might look like without type erasure, but unfortunately, it is impossible. Once again, the generic type information is not available at runtime.

Q4. If a Generic Type Is Omitted When Instantiating an Object, Will the Code Still Compile?

As generics did not exist before Java 5, it is possible not to use them at all. For example, generics were retrofitted to most of the standard Java classes such as collections. If we look at our list from question one, then we will see that we already have an example of omitting the generic type:

List list = new ArrayList();

Despite being able to compile, it’s still likely that there will be a warning from the compiler. This is because we are losing the extra compile-time check that we get from using generics.

The point to remember is that while backward compatibility and type erasure make it possible to omit generic types, it is bad practice.

Q5. How Does a Generic Method Differ from a Generic Type?

A generic method is where a type parameter is introduced to a method, living within the scope of that method. Let’s try this with an example:

public static <T> T returnType(T argument) { 
    return argument; 
}

We’ve used a static method but could have also used a non-static one if we wished. By leveraging type inference (covered in the next question), we can invoke this like any ordinary method, without having to specify any type arguments when we do so.

Q6. What Is Type Inference?

Type inference is when the compiler can look at the type of a method argument to infer a generic type. For example, if we passed in T to a method which returns T, then the compiler can figure out the return type. Let’s try this out by invoking our generic method from the previous question:

Integer inferredInteger = returnType(1);
String inferredString = returnType("String");

As we can see, there’s no need for a cast, and no need to pass in any generic type argument. The argument type only infers the return type.

Q7. What Is a Bounded Type Parameter?

So far all our questions have covered generic types arguments which are unbounded. This means that our generic type arguments could be any type that we want.

When we use bounded parameters, we are restricting the types that can be used as generic type arguments.

As an example, let’s say we want to force our generic type always to be a subclass of animal:

public abstract class Cage<T extends Animal> {
    abstract void addAnimal(T animal)
}

By using extends, we are forcing T to be a subclass of animal. We could then have a cage of cats:

Cage<Cat> catCage;

But we could not have a cage of objects, as an object is not a subclass of an animal:

Cage<Object> objectCage; // Compilation error

One advantage of this is that all the methods of animal are available to the compiler. We know our type extends it, so we could write a generic algorithm which operates on any animal. This means we don’t have to reproduce our method for different animal subclasses:

public void firstAnimalJump() {
    T animal = animals.get(0);
    animal.jump();
}

Q8. Is It Possible to Declared a Multiple Bounded Type Parameter?

Declaring multiple bounds for our generic types is possible. In our previous example, we specified a single bound, but we could also specify more if we wish:

public abstract class Cage<T extends Animal & Comparable>

In our example, the animal is a class and comparable is an interface. Now, our type must respect both of these upper bounds. If our type were a subclass of animal but did not implement comparable, then the code would not compile. It’s also worth remembering that if one of the upper bounds is a class, it must be the first argument.

Q9. What Is a Wildcard Type?

A wildcard type represents an unknown type. It’s detonated with a question mark as follows:

public static void consumeListOfWildcardType(List<?> list)

Here, we are specifying a list which could be of any type. We could pass a list of anything into this method.

Q10. What Is an Upper Bounded Wildcard?

An upper bounded wildcard is when a wildcard type inherits from a concrete type. This is particularly useful when working with collections and inheritance.

Let’s try demonstrating this with a farm class which will store animals, first without the wildcard type:

public class Farm {
  private List<Animal> animals;

  public void addAnimals(Collection<Animal> newAnimals) {
    animals.addAll(newAnimals);
  }
}

If we had multiple subclasses of animal, such as cat and dog, we might make the incorrect assumption that we can add them all to our farm:

farm.addAnimals(cats); // Compilation error
farm.addAnimals(dogs); // Compilation error

This is because the compiler expects a collection of the concrete type animal, not one it subclasses.

Now, let’s introduce an upper bounded wildcard to our add animals method:

public void addAnimals(Collection<? extends Animal> newAnimals)

Now if we try again, our code will compile. This is because we are now telling the compiler to accept a collection of any subtype of animal.

Q11. What Is an Unbounded Wildcard?

An unbounded wildcard is a wildcard with no upper or lower bound, that can represent any type.

It’s also important to know that the wildcard type is not synonymous to object. This is because a wildcard can be any type whereas an object type is specifically an object (and cannot be a subclass of an object). Let’s demonstrate this with an example:

List<?> wildcardList = new ArrayList<String>(); 
List<Object> objectList = new ArrayList<String>(); // Compilation error

Again, the reason the second line does not compile is that a list of objects is required, not a list of strings. The first line compiles because a list of any unknown type is acceptable.

Q12. What Is a Lower Bounded Wildcard?

A lower bounded wildcard is when instead of providing an upper bound, we provide a lower bound by using the super keyword. In other words, a lower bounded wildcard means we are forcing the type to be a superclass of our bounded type. Let’s try this with an example:

public static void addDogs(List<? super Animal> list) {
   list.add(new Dog("tom"))
}

By using super, we could call addDogs on a list of objects:

ArrayList<Object> objects = new ArrayList<>();
addDogs(objects);

This makes sense, as an object is a superclass of animal. If we did not use the lower bounded wildcard, the code would not compile, as a list of objects is not a list of animals.

If we think about it, we wouldn’t be able to add a dog to a list of any subclass of animal, such as cats, or even dogs. Only a superclass of animal. For example, this would not compile:

ArrayList<Cat> objects = new ArrayList<>();
addDogs(objects);

Q13. When Would You Choose to Use a Lower Bounded Type vs. an Upper Bounded Type?

When dealing with collections, a common rule for selecting between upper or lower bounded wildcards is PECS. PECS stands for producer extends, consumer super.

This can be easily demonstrated through the use of some standard Java interfaces and classes.

Producer extends just means that if you are creating a producer of a generic type, then use the extends keyword. Let’s try applying this principle to a collection, to see why it makes sense:

public static void makeLotsOfNoise(List<? extends Animal> animals) {
    animals.forEach(Animal::makeNoise);   
}

Here, we want to call makeNoise() on each animal in our collection. This means our collection is a producer, as all we are doing with it is getting it to return animals for us to perform our operation on. If we got rid of extends, we wouldn’t be able to pass in lists of cats, dogs or any other subclasses of animals. By applying the producer extends principle, we have the most flexibility possible.

Consumer super means the opposite to producer extends. All it means is that if we are dealing with something which consumes elements, then we should use the super keyword. We can demonstrate this by repeating our previous example:

public static void addCats(List<? super Animal> animals) {
    animals.add(new Cat());   
}

We are only adding to our list of animals, so our list of animals is a consumer. This is why we use the super keyword. It means that we could pass in a list of any superclass of animal, but not a subclass. For example, if we tried passing in a list of dogs or cats then the code would not compile.

The final thing to consider is what to do if a collection is both a consumer and a producer. An example of this might be a collection where elements are both added and removed. In this case, an unbounded wildcard should be used.

Q14. Are There Any Situations Where Generic Type Information Is Available at Runtime?

There is one situation where a generic type is available at runtime. This is when a generic type is part of the class signature like so:

public class CatCage implements Cage<Cat>

By using reflection, we get this type parameter:

(Class<T>) ((ParameterizedType) getClass()
  .getGenericSuperclass()).getActualTypeArguments()[0];

This code is somewhat brittle. For example, it’s dependant on the type parameter being defined on the immediate superclass. But, it demonstrates the JVM does have this type information.

Next »
Java Flow Control Interview Questions (+ Answers)
« Previous
Memory Management in Java Interview Questions (+Answers)
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LS – NPI (cat=Java)
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)