Skip to content

enum

An enumeration (enum) in Java is a special data type that enables a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it.

It is a fixed list of options, like the suits in a deck of cards (CLUBS, DIAMONDS, HEARTS, SPADES).

Enums improve type safety by ensuring a variable holds only one of a specific set of values.

Demonstrating Basic enum Use, values(), and valueOf()

The values() method returns an array containing all the enum constants, while valueOf() returns the enum constant whose value corresponds to the specified string.

java
enum Level {
    LOW,
    MEDIUM,
    HIGH
}

public class EnumDemo {
    public static void main(String[] args) {

        Level myLevel = Level.MEDIUM;
        System.out.println("Current Level: " + myLevel);

        System.out.println("\nAll available levels:");
        // The values() method returns an array of all enum constants
        for (Level lvl : Level.values()) {
            System.out.println("- " + lvl);
        }

        // The valueOf() method gets the enum constant from a string
        String levelString = "HIGH";
        Level highLevel = Level.valueOf(levelString);
        System.out.println("\nvalueOf(\"HIGH\"): " 
	        + highLevel);
    }
}

Output:

Current Level: MEDIUM

All available levels:
- LOW
- MEDIUM
- HIGH

valueOf("HIGH"): HIGH

enum with a Custom Method

This program shows that an enum can have constructors, fields, and methods, much like a class. Here, the DayOfWeek enum includes a method isWorkDay() to check if the day is part of the standard work week.

java
enum DayOfWeek {
    SUNDAY,
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY;   // Semicolon needed if there are members below

    public boolean isWorkDay() {
// 'this' refers to constant on which the method is called
        switch (this) {
            case SATURDAY:
            case SUNDAY:
                return false; // It's the weekend
            default:
                return true; // It's a workday
        }
    }
}

public class DayOfWeekDemo {
    public static void main(String[] args) {
        DayOfWeek today = DayOfWeek.MONDAY;
        DayOfWeek weekendDay = DayOfWeek.SUNDAY;

        System.out.println(today + " is a work day? " 
	        + today.isWorkDay());
        System.out.println(weekendDay + " is a work day? " 
	        + weekendDay.isWorkDay());
    }
}

Output:

MONDAY is a work day? true
SUNDAY is a work day? false

Type Wrappers

Type Wrappers are classes that "wrap" a primitive data type into an object. For each of Java's eight primitive types (int, double, char, etc.), there is a corresponding wrapper class (Integer, Double, Character, etc.).

Java's Collections Framework (like ArrayList or HashMap) and other modern features can only work with objects, not primitive types. Wrapper classes are necessary to bridge this gap, allowing you to store primitive values in these object-based data structures.

Each primitive type has a corresponding wrapper class in the java.lang package:

Primitive TypeWrapper Class
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

This example shows an ArrayList, which can only store objects. We use the Integer wrapper class to store int values.

java
import java.util.ArrayList;

public class WrapperDemo {
    public static void main(String[] args) {
        ArrayList<Integer> numberList = new ArrayList<>();

        // Add primitive int values to the list.
        // Java automatically converts them to Integer objects (autoboxing).
        numberList.add(10);
        numberList.add(20);
        numberList.add(30);

        System.out.println("ArrayList of Integer objects: " 
	        + numberList);
    }
}

Autoboxing and Unboxing

Autoboxing is the automatic conversion that the Java compiler makes between a primitive type and its corresponding wrapper class.

Unboxing is the reverse: converting an object of a wrapper type to its corresponding primitive value.

This feature greatly simplifies code by eliminating the need for manual conversion.

java
class AutoBox {
    public static void main(String[] args) {
        Integer iOb = 99;
        System.out.println("Original value of iOb: " + iOb);

        ++iOb; // unboxed, incremented, reboxed
        System.out.println("After ++iOb: " + iOb);

        iOb += 10; // unboxed, added, reboxed
        System.out.println("After iOb += 10: " + iOb);

        Integer iOb2 = iOb + (iOb / 3); // unboxed, computed, reboxed
        System.out.println("iOb2 after expression: " + iOb2);

        int i = iOb + (iOb / 3); // unboxed, result stored as primitive
        System.out.println("i after expression: " + i);
    }
}
java
import java.util.ArrayList;

public class AutoboxingDemo {
    public static void main(String[] args) {
        // Autoboxing: The primitive int '100' is automatically converted
        // to an Integer object before being added to the list.
        ArrayList<Integer> myInts = new ArrayList<>();
        myInts.add(100); 

        // Unboxing: The Integer object at index 0 is automatically converted
        // back to a primitive 'int' so it can be used in an arithmetic operation.
        int myPrimitiveInt = myInts.get(0); 
        System.out.println("Value from list (as primitive): " 
	        + myPrimitiveInt);

        // --- Another example ---
        // Autoboxing: primitive 50 is converted to an Integer object
        Integer integerObject = 50; 

        // Unboxing: integerObject is converted to a primitive to be incremented
        integerObject++; 

        System.out.println("Incremented Integer object: " + integerObject);
    }
}

Generics

Generics in Java are a feature that allows to create classes, interfaces, and methods that can work with any data type. By parameterizing types, a single component can be created that can operate on different data types without sacrificing type safety.

The primary purposes of generics are:

  • Type Safety: Generics enforce type checks at compile-time. This catches invalid type errors during development rather than having them cause crash at runtime. For example, a list declared as List<String> will only accept String objects, and the compiler will flag any attempt to add an Integer.

  • Eliminating Casts: Before generics, Object had to be used for general-purpose classes and then manually cast the object back to its original type when retrieving it. This was clumsy and unsafe. Generics eliminate the need for this explicit casting.

The general form of a generic class uses a type parameter, typically denoted by a single uppercase letter like T, enclosed in angle brackets <>.

java
class ClassName<T> {
    T myType; // A variable of type T
    // ... methods and constructors
}

Program Demonstrating a Generic Class

This program defines a generic Box class. The same Box class can be used to store an Integer, a String, or any other object type, providing type safety for each instance.

java
// A simple generic class named 'Box'
class Box<T> {
    private T content;

    public void set(T content) {
        this.content = content;
    }

    public T get() {
        return content;
    }
}

public class GenericClassDemo {
    public static void main(String[] args) {
        // --- Create a Box for Integers ---
        Box<Integer> integerBox = new Box<>();
        integerBox.set(10);

		// integerBox.set("hello"); 
		// This would cause a compile error

        Integer intValue = integerBox.get();
        System.out.println("Integer value: " + intValue);

        // --- Create a Box for Strings ---
        Box<String> stringBox = new Box<>();
        stringBox.set("Hello World");

        String strValue = stringBox.get();
        System.out.println("String value: " + strValue);
    }
}

Generic Class with Multiple Type Parameters

You can create a generic class with multiple type parameters by listing them inside the angle brackets, separated by commas. This is useful for creating classes that associate two or more different types, like a key-value pair.

java
// A generic class with two type parameters, K for Key and V for Value
class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }

    public void showTypes() {
        System.out.println("Type of K is " 
	        + key.getClass().getName());
        System.out.println("Type of V is " 
	        + value.getClass().getName());
    }
}

public class MultiGenericDemo {
    public static void main(String[] args) {

        Pair<String, Integer> pair1 = new Pair<>("UserID", 12345);
        
        System.out.println("Pair 1: Key = " + pair1.getKey() 
	        + ", Value = " + pair1.getValue());
        
        pair1.showTypes();
        
        System.out.println();

        // Create a Pair of a String and another String
        Pair<String, String> pair2 = new Pair<>("Status", "Active");
        
        System.out.println("Pair 2: Key = " + pair2.getKey() 
	        + ", Value = " + pair2.getValue());
	    
        pair2.showTypes();
    }
}

Made with ❤️ for students, by a fellow learner.