("Java", as used here, is defined as standard Java SE 7; "Haskell", as used here, is defined as standard Haskell 2010.)

Things that Java's type system has but that Haskell's doesn't:

nominal subtype polymorphism

partial runtime type information

Things that Haskell's type system has but that Java's doesn't:

bounded ad-hoc polymorphism gives rise to "constraint-based" subtype polymorphism

higher-kinded parametric polymorphism

principal typing

EDIT:

Examples of each of the points listed above:

Unique to Java (as compared to Haskell)

Nominal subtype polymorphism

/* declare explicit subtypes (limited multiple inheritance is allowed) */ abstract class MyList extends AbstractList<String> implements RandomAccess { /* specify a type's additional initialization requirements */ public MyList(elem1: String) { super() /* explicit call to a supertype's implementation */ this.add(elem1) /* might be overridden in a subtype of this type */ } } /* use a type as one of its supertypes (implicit upcasting) */ List<String> l = new ArrayList<>() /* some inference is available for generics */

Partial runtime type information

/* find the outermost actual type of a value at runtime */ Class<?> c = l.getClass // will be 'java.util.ArrayList' /* query the relationship between runtime and compile-time types */ Boolean b = l instanceOf MyList // will be 'false'

Unique to Haskell (as compared to Java)

Bounded ad-hoc polymorphism

-- declare a parametrized bound class A t where -- provide a function via this bound tInt :: t Int -- require other bounds within the functions provided by this bound mtInt :: Monad m => m (t Int) mtInt = return tInt -- define bound-provided functions via other bound-provided functions -- fullfill a bound instance A Maybe where tInt = Just 5 mtInt = return Nothing -- override defaults -- require exactly the bounds you need (ideally) tString :: (Functor t, A t) => t String tString = fmap show tInt -- use bounds that are implied by a concrete type (e.g., "Show Int")

"Constraint-based" subtype polymorphism (based on bounded ad-hoc polymorphism)

-- declare that a bound implies other bounds (introduce a subbound) class (A t, Applicative t) => B t where -- bounds don't have to provide functions -- use multiple bounds (intersection types in the context, union types in the full type) mtString :: (Monad m, B t) => m (t String) mtString = return mtInt -- use a bound that is implied by another bound (implicit upcasting) optString :: Maybe String optString = join mtString -- full types are contravariant in their contexts

Higher-kinded parametric polymorphism

-- parametrize types over type variables that are themselves parametrized data OneOrTwoTs t x = OneVariableT (t x) | TwoFixedTs (t Int) (t String) -- bounds can be higher-kinded, too class MonadStrip s where -- use arbitrarily nested higher-kinded type variables strip :: (Monad m, MonadTrans t) => s t m a -> t m a -> m a

Principal typing

This one is difficult to give a direct example of, but it means that every expression has exactly one maximally general type (called its principal type), which is considered the canonical type of that expression. In terms of "constraint-based" subtype polymorphism (see above), the principal type of an expression is the unique subtype of every possible type that that expression can be used as. The presence of principal typing in (unextended) Haskell is what allows complete type inference (that is, successful type inference for every expression, without any type annotations needed). Extensions that break principal typing (of which there are many) also break the completeness of type inference.