C#
JV

C# to Java

10 lessons

Progress0%
1Introduction2Type Systems3Properties & Getters4Generics5Collections6LINQ to Streams7Async Programming8Ecosystem9Modern Java Features10Concurrency
All Mirror Courses
C#
JV
Generics
MirrorLesson 4 of 10
Lesson 4

Generics

Generics

Introduction

In this lesson, you'll learn about generics in Java. Coming from C#, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
C#
From C#:

In C#, you're familiar with generics.

JV
In Java:

Java has its own approach to generics, which we'll explore step by step.

The Java Way

Let's see how Java handles this concept. Here's a typical example:

JV
Java Example
class Box<T> {
    T value;
}

// Wildcards instead of variance annotations
List<? extends Number> upperBounded;  // covariant read
List<? super Integer> lowerBounded;   // contravariant write

Comparing to C#

Here's how you might have written similar code in C#:

C#
C# (What you know)
class Box<T> where T : class {
    public T Value { get; set; }
}

// Covariance on interface
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings; // covariant
Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

JV
In Java:

C# constraints use 'where T : constraint'; Java uses 'T extends Type' inline

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

JV
In Java:

C# supports covariance/contravariance via in/out on interfaces; Java uses ? extends/? super wildcards

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

JV
In Java:

Both use type erasure at runtime — generic type info is lost

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

JV
In Java:

C# has struct/class/new() constraints; Java has no equivalent for value types

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

JV
In Java:

Java wildcards (?) allow flexible API design without needing variance annotations

Step-by-Step Breakdown

1. Generic Constraints

C# uses 'where T : SomeType' after the class/method signature. Java puts the bound inline with 'T extends SomeType'.

C#
C#
class Sorter<T> where T : IComparable<T> {
    T Max(T a, T b) => a.CompareTo(b) > 0 ? a : b;
}
JV
Java
class Sorter<T extends Comparable<T>> {
    T max(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }
}

2. Wildcards vs Variance

Java uses ? extends T for covariant (read-only) positions and ? super T for contravariant (write-only) positions — the PECS rule.

C#
C#
// C# out = covariant, in = contravariant
IEnumerable<out T>  // can read T
IComparer<in T>     // can write T
JV
Java
// Java wildcard PECS: Producer Extends, Consumer Super
void printAll(List<? extends Number> list) {
    for (Number n : list) System.out.println(n); // read OK
}

void addInts(List<? super Integer> list) {
    list.add(42); // write OK
}
Rule of Thumb
PECS: use ? extends when you only read from a collection, ? super when you only write to it.

3. Multiple Bounds

Both languages support multiple type bounds. Syntax differs slightly.

C#
C#
class Repo<T> where T : class, IEntity, new() { }
JV
Java
// Java: class bound first, then interfaces
class Repo<T extends Entity & Serializable> { }
// Note: only one class bound allowed, rest must be interfaces
Common Pitfall
In Java, if you have both a class and interface bounds, the class must come first. You cannot have two class bounds.

4. Type Erasure Similarity

Both C# and Java erase generic type parameters at runtime for reference types. You cannot do 'new T()' or 'typeof(T)' without workarounds in either language.

C#
C#
// C# workaround with new() constraint
T Create<T>() where T : new() => new T();
JV
Java
// Java workaround: pass Class<T> explicitly
<T> T create(Class<T> clazz) throws Exception {
    return clazz.getDeclaredConstructor().newInstance();
}

Common Mistakes

When coming from C#, developers often make these mistakes:

  • C# constraints use 'where T : constraint'; Java uses 'T extends Type' inline
  • C# supports covariance/contravariance via in/out on interfaces; Java uses ? extends/? super wildcards
  • Both use type erasure at runtime — generic type info is lost
Common Pitfall
Don't assume Java works exactly like C#. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • Java bounds are inline (T extends Type) vs C# where clause
  • Java wildcards (? extends/? super) replace C# in/out variance annotations
  • PECS: Producer Extends, Consumer Super
  • Both erase generic type info at runtime for reference types
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your C# code in Java to practice these concepts.
PreviousNext