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.
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.
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 Type | Wrapper Class |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
This example shows an ArrayList
, which can only store objects. We use the Integer
wrapper class to store int
values.
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.
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);
}
}
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 acceptString
objects, and the compiler will flag any attempt to add anInteger
.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 <>
.
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.
// 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.
// 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();
}
}