Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Java Error: Method Cannot Be Applied to Given Types with Generic Arrays – Fix

Why Your Generic Method Won’t Accept Primitive Arrays in Java

A deep dive into Java generics, primitive types, and best practices for writing reusable utility methods

The Problem

You’ve written a clever method to count duplicates in an array. It works perfectly with int[]:

public static int countAllDuplicates(int[] myList) {
    int duplicates = 0;
    for (int i = 0; i < myList.length; i++) {
        for (int j = i + 1; j < myList.length; j++) {
            if (myList[i] == myList[j]) {
                duplicates++;
            }
        }
    }
    return duplicates;
}

But then you think: “This should work for any type! Let me make it generic.”

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

public static <T> int countAllDuplicates(T[] myList) {
    // Same logic...
}

And suddenly, the compiler throws this error:

java: method countAllDuplicates in class Util cannot be applied to given types;
required: T[]
found: int[]

Wait, what? Shouldn’t int[] be a valid T[]?

No, it shouldn’t. And understanding why reveals something fundamental about how Java handles primitives and generics.


The Root Cause: Primitives Are Not Objects

Java has a strict separation between primitive types (int, double, boolean, etc.) and reference types (objects). This distinction is crucial:

Primitive Types Reference Types
int, long, double, float, char, boolean, byte, short All classes, interfaces, arrays of objects
Stored directly on the stack Stored as references to heap objects
Cannot be null Can be null
Cannot be used with generics Can be used with generics
Key Insight: An int[] is an object (arrays themselves are objects in Java, as defined in JLS §10), but its component type int is not.

When you declare a generic parameter T, Java expects T to be a reference type. Therefore:

  • Integer[] → Valid for T[]
  • String[] → Valid for T[]
  • int[]Invalid for T[] (because int is not a reference type)

Why ArrayList Worked (Sort Of)

You discovered that using ArrayList seemed to solve the problem:

public static int countAllDuplicatesArrayList(ArrayList myList) {
    int duplicates = 0;
    for (int i = 0; i < myList.size(); i++) {
        for (int j = i + 1; j < myList.size(); j++) {
            if (myList.get(i).equals(myList.get(j))) {
                duplicates++;
            }
        }
    }
    return duplicates;
}

This works because:

  1. Autoboxing: When you create ArrayList<Integer> with Arrays.asList(1, 2, 3), Java automatically converts (autoboxes) the primitive int values to Integer objects.
  2. Raw Types: By using ArrayList without a type parameter, you’re using a raw type. The compiler allows this for backward compatibility with pre-generics code, but it’s dangerous.
Warning: That warning you saw—Raw use of parameterized class 'ArrayList'—is telling you something important: this approach sacrifices type safety.

Why Raw Types Are Dangerous

Using raw types essentially disables generic type checking. Consider this scenario:

ArrayList rawList = new ArrayList();
rawList.add("Hello");
rawList.add(42);
rawList.add(new Object());

// No compile error, but runtime disaster waiting to happen!
for (int i = 0; i < rawList.size(); i++) {
    String s = (String) rawList.get(i); // ClassCastException at runtime!
}

With raw types, the compiler can’t help you catch type mismatches. You lose one of Java’s greatest safety features.


The Correct Solution

Here’s how to write a proper generic method for counting duplicates:

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        // Using List with Integer (wrapper class, not primitive)
        List<Integer> numbers = Arrays.asList(1, 2, 3, 1, 4, 3);
        System.out.println(Util.countAllDuplicates(numbers)); // Output: 2
        
        // Works with any type!
        List<String> words = Arrays.asList("apple", "banana", "apple", "cherry");
        System.out.println(Util.countAllDuplicates(words)); // Output: 1
    }
}

class Util {
    /**
     * Counts the number of duplicate pairs in a list.
     * For example, [1, 2, 3, 1, 4, 3] has 2 duplicate pairs: (1,1) and (3,3).
     */
    public static <T> int countAllDuplicates(List<T> list) {
        int duplicates = 0;
        for (int i = 0; i < list.size(); i++) {
            for (int j = i + 1; j < list.size(); j++) {
                // IMPORTANT: Use .equals() for object comparison, not ==
                if (list.get(i).equals(list.get(j))) {
                    duplicates++;
                }
            }
        }
        return duplicates;
    }
}

Key Improvements

  1. List<T> instead of ArrayList<T>: Always program to the interface, not the implementation. This makes your code more flexible.
  2. Proper Type Parameter: <T> ensures type safety. The compiler will prevent you from accidentally mixing types.
  3. equals() instead of ==: For objects, == compares references (memory addresses), while equals() compares values. This is critical!
  4. Util instead of util: Java naming conventions dictate that class names should be in PascalCase.

Alternative: A More Efficient Solution

The O(n²) nested loop approach works, but for larger lists, you might want a more efficient solution using a Map:

import java.util.List;
import java.util.HashMap;
import java.util.Map;

class Util {
    /**
     * Counts the total number of duplicate pairs in O(n) time.
     */
    public static <T> int countAllDuplicates(List<T> list) {
        Map<T, Integer> frequency = new HashMap<>();
        
        // Count occurrences of each element
        for (T element : list) {
            frequency.merge(element, 1, Integer::sum);
        }
        
        // Calculate duplicate pairs: for n occurrences, there are n*(n-1)/2 pairs
        int duplicatePairs = 0;
        for (int count : frequency.values()) {
            if (count > 1) {
                duplicatePairs += count * (count - 1) / 2;
            }
        }
        
        return duplicatePairs;
    }
}

This approach:

  • Runs in O(n) time instead of O(n²)
  • Uses O(n) additional space for the frequency map
  • Handles edge cases correctly

Quick Reference: Primitive Types vs Wrapper Classes

When working with generics, always use wrapper classes:

Primitive Wrapper Class Example
int Integer List<Integer>
double Double List<Double>
boolean Boolean List<Boolean>
char Character List<Character>
long Long List<Long>
float Float List<Float>
byte Byte List<Byte>
short Short List<Short>

Summary

Question 1: Why is a raw parameterized class allowed but not a generic array with primitives?

They’re fundamentally different issues: primitive arrays can’t match generic types because primitives aren’t objects. Raw types are allowed for backward compatibility but bypass type safety entirely.

Question 2: How do I make the method work with generic types?

Use List<T> with the proper type parameter declaration:

public static <T> int countAllDuplicates(List<T> list)

Question 3: How do I avoid the raw type warning?

Always specify the type parameter:

// ❌ Raw type - avoid this
ArrayList myList = new ArrayList();

// ✓ Parameterized type - use this
ArrayList<Integer> myList = new ArrayList<>();

// ✓ Even better - use the interface
List<Integer> myList = new ArrayList<>();

Further Reading

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading