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.

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

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

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Overview

In this tutorial, we’ll learn how to create an ArrayList that can hold multiple object types in Java. We’ll also learn how to add data of multiple types into an ArrayList and then retrieve data from the ArrayList to transform it back to the original data types.

2. Background

A basic understanding of the Collection framework, especially ArrayList, is required for this article. Take a look at the corresponding articles, Java List Interface and Guide to the Java ArrayList, to gain a basic understanding of these classes.

The ArrayList class does not support primitive datatypes directly, but it does support them via wrapper classes. The ArrayList is ideally created with a reference class type. It indicates that when adding data to an ArrayList, only that reference class’s data is supported. For instance, an ArrayList<Integer> won’t accept data from the String, Boolean, or Double classes.

Our goal is to store data from multiple types in a single ArrayList. This goes against the fundamental nature of an ArrayList with a single type parameter. However, it is still possible to achieve this with a variety of approaches. We’ll go over them in depth in the following sections of this article.

3. Raw Type vs. Parameterized Type

A raw type is a generic type without any type argument. Raw types can be used like regular types without any restrictions, except that certain uses will result in “unchecked” warnings. On the other hand, a parameterized type is an instantiation of a generic type with actual type arguments. Parameterized types can provide a generic or a concrete type argument during the instantiation of a class.

The Java specification cautions against using raw types when creating a List because type safety is lost. Casting exceptions at runtime may result from this. When declaring a list, it’s recommended to always use a type parameter:

 /* raw type */
List myList = new ArrayList<>();

 /* parameterized type */
List<Object> myList = new ArrayList<>();

4. Using Object as the Generic Type

4.1. Creating the ArrayList

Most frequently, the developer will produce an ArrayList with certain type parameters like String, Integer, or Double. This is so that a single type of data information may be represented, retrieved, and processed with ease. However, our goal is to create an ArrayList with multiple object types, thus being able to store data of many data types in a single ArrayList.

To achieve this, we can use the parent class of all Java classes: the Object class. The Object class is the topmost class in the Java class hierarchy, and all other classes are directly or indirectly derived from it.

Let’s see how to initialize an ArrayList of Object type parameter:

ArrayList<Object> multiTypeList = new ArrayList<>();

4.2. Inserting Data into the ArrayList

In this section, we’ll learn how we can insert the data into an ArrayList. We had created an ArrayList of Object element type, hence, every time we add any type of data into the ArrayList, it’ll first get auto-cast to Object type and then will get stored inside the ArrayList. We’ll attempt to insert data of various object types such as Integer, Double, String, List, and a user-defined custom object.

Furthermore, we already know that primitive data types such as int and double cannot be directly stored in an ArrayList, so we’ll convert them using their respective wrapper classes. These wrapper classes are then typecast into the Object class and stored in an ArrayList.

Other types, such as String, List, and CustomObject, are type parameters in and of themselves, so they can be added directly. However, these elements will also be typecast into the Object before being stored.

Let’s look at the code example:

multiTypeList.add(Integer.valueOf(10));
multiTypeList.add(Double.valueOf(11.5));
multiTypeList.add("String Data");
multiTypeList.add(Arrays.asList(1, 2, 3));
multiTypeList.add(new CustomObject("Class Data"));
multiTypeList.add(BigInteger.valueOf(123456789));
multiTypeList.add(LocalDate.of(2023, 9, 19));

4.3. Retrieving Data from the ArrayList

We’ll also discover how to retrieve the information from the ArrayList of Object type parameter and put it back into the various type parameters from which it was originally added. The ArrayList‘s data elements will all be of the static Object element type.

The developer will be in charge of casting it back to the appropriate data type for additional processing. We can accomplish this in two different ways: using the instanceof keyword and the getClass() method.

When we use the getClass() method, it returns the type of the data element’s class. We can compare it and then transform the data back to the appropriate data type. When we use instanceof, it compares the left-side element to the right-side type parameter and returns a boolean result.

In this tutorial, we’ll use the instanceof keyword to cast the data into the appropriate type parameter. We will then print the value of the data element to see if it was correctly transformed to the original data type.

Let’s look at the code example:

for (Object dataObj: multiTypeList) {
    if (dataObj instanceof Integer intData)
        System.out.println("Integer Data : " + intData);
    else if (dataObj instanceof Double doubleData)
        System.out.println("Double Data : " + doubleData);
    else if (dataObj instanceof String stringData)
        System.out.println("String Data : " + stringData);
    else if (dataObj instanceof List < ? > intList)
        System.out.println("List Data : " + intList);
    else if (dataObj instanceof CustomObject customObj)
        System.out.println("CustomObject Data : " + customObj.getClassData());
    else if (dataObj instanceof BigInteger bigIntData)
        System.out.println("BigInteger Data : " + bigIntData);
    else if (dataObj instanceof LocalDate localDate)
        System.out.println("LocalDate Data : " + localDate.toString());
}

Please note that we’re using pattern matching, which is a feature from JDK 16.

Let’s now check the output of this program:

// Program Output
Integer Data : 10
Double Data : 11.5
String Data : String Data
List Data : [1, 2, 3]
CustomObject Data : Class Data
BigInteger Data : 123456789
LocalDate Data : 2023-09-19

As we can see, the list elements were looped one by one, and the output was logged as per the relevant data type of each ArrayList element.

5. Alternative Approaches

It’s important to note that utilizing an ArrayList of the Object class can cause problems with data processing after retrieval if the casting or parsing of the relevant type parameter is not handled correctly. Let’s go through a few simple ways we can avoid these issues.

5.1. Using a Common Interface as the Type Parameter

One way around these potential problems is to make a list of a predefined or custom interface. This will restrict the entry of data to just those classes that implement the defined interface. As shown below, the list of Map permits data of different representations of the Map interface:

ArrayList<Map> diffMapList = new ArrayList<>();
diffMapList.add(new HashMap<>());
diffMapList.add(new TreeMap<>());
diffMapList.add(new LinkedHashMap<>());

5.2. Using a Parent Class as the Type Parameter

In a different approach, we can define a list of the parent or superclass. It will accept the values from all child class representations. Let’s create a list of Number objects that can accept Integer, Double, Float, and other numeric types:

ArrayList<Number> myList = new ArrayList<>();
myList.add(1.2);
myList.add(2);
myList.add(-3.5);

5.3. Using a Custom Wrapper Class as the Type Parameter

We can also create a custom wrapper class with the object types we want to allow. Such a class will have dedicated getter and setter methods for all elements of different types. We can then create a List with our class as a type parameter. This is a widely used approach, as it ensures type safety. The below example shows a List of custom wrapper classes for Integer and String elements:

public class CustomObject {
    String classData;
    Integer intData;
    // constructors and getters
}
ArrayList<CustomObject> objList = new ArrayList<>();
objList.add(new CustomObject("String"));
objList.add(new CustomObject(2));

5.4. Using a Functional Interface

Yet another solution is to create a list via a functional interface. This approach can be useful if we need some constraints or validation before inserting elements.

We can write a predicate that checks the List for allowed data types. This Predicate can be used by the abstract method of the functional interface to determine whether to add the data element to the list.

In the functional interface, we can then define a default method that prints the information of the allowed data type:

@FunctionalInterface
public interface UserFunctionalInterface {

    List<Object> addToList(List<Object> list, Object data);

    default void printList(List<Object> dataList) {
        for (Object data: dataList) {
            if (data instanceof String stringData)
                System.out.println("String Data: " + stringData);
            if (data instanceof Integer intData)
                System.out.println("Integer Data: " + intData);
        }
    }
}

Consider the following scenario: We create an ArrayList of objects, but only add type parameters of String and Integer. The functional interface can add the data to the list after the predicate checks the type parameter. We’ll also add a default method for printing the data:

List<Object> dataList = new ArrayList<>();

Predicate <Object> myPredicate = inputData -> (inputData instanceof String || inputData instanceof Integer);

UserFunctionalInterface myInterface = (listObj, data) -> {
    if (myPredicate.test(data))
        listObj.add(data);
    else
        System.out.println("Skipping input as data not allowed for class: " + data.getClass()
            .getSimpleName());
    return listObj;
};

myInterface.addToList(dataList, Integer.valueOf(2));
myInterface.addToList(dataList, Double.valueOf(3.33));
myInterface.addToList(dataList, "String Value");
myInterface.printList(dataList);

Let’s check the output of this approach:

//Output
Integer Data: 2
Skipping input as data is not allowed for class: Double 
String Data: String Value

6. Conclusion

In this quick article, we took a look at ArrayList’s functionality to store data of various types. We learned how we can create an ArrayList instance with an Object type parameter. In addition to this, we learned how to add or remove elements from ArrayList of multiple object types. We also learned best practices that we can adopt while dealing with the ArrayList with multiple object types.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
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.

eBook Jackson – NPI EA – 3 (cat = Jackson)
2 Comments
Oldest
Newest
Inline Feedbacks
View all comments