How do you declare a parameterized (generic) function-type alias such as a Predicate, and what is its relationship to the underlying function type?
answer
- Type params in <> after the name
- Predicate<T> = (T) -> Boolean
- substitute then expand to raw type
- bounds allowed: <T : Number>
- no own variance; expanded type rules apply
basics
~10 sAdd type parameters in angle brackets: typealias Predicate<T> = (T) -> Boolean. Then Predicate<String> means (String) -> Boolean. The compiler just substitutes the type argument and expands the alias.
solid answer
~40 sA parameterized type alias declares type parameters after the name: `typealias Predicate<T> = (T) -> Boolean`. Using `Predicate<String>` is exactly `(String) -> Boolean` after the compiler substitutes `T = String` and expands the alias — they are the same type. You can have several parameters: `typealias Transform<A, B> = (A) -> B`, or combine with collections: `typealias Mapper<K, V> = (List<K>) -> Map<K, V>`. The alias's type parameters are independent placeholders local to the declaration; they do not impose variance and cannot carry their own bounds beyond what the right-hand side allows expressing (you can write bounds like `<T : Number>`). Because it is still a pure alias, `Predicate<Int>` and the raw `(Int) -> Boolean` are interchangeable, with no nominal distinction and no runtime type.
code
kotlin · 8 linestypealias Predicate<T> = (T) -> Boolean
typealias Transform<A, B> = (A) -> B
fun <T> filter(xs: List<T>, keep: Predicate<T>): List<T> = xs.filter(keep)
fun <A, B> mapAll(xs: List<A>, f: Transform<A, B>): List<B> = xs.map(f)
val positives = filter(listOf(-1, 2, 3)) { it > 0 } // Predicate<Int>
val lengths = mapAll(listOf("a", "bb")) { it.length } // Transform<String, Int>go deeper
Can write typealias Predicate<T> = (T) -> Boolean and use Predicate<String>.
Explains substitute-then-expand and that the result equals the raw function type; knows multiple type params.
Discusses upper bounds, that aliases carry no variance, and how subtyping follows the expanded type.
Weighs generic aliases vs nominal generic types in a public API for evolvability and clarity.
## Declaring a parameterized function-type alias A **parameterized** (generic) type alias introduces one or more **type parameters** — placeholders for types supplied later — right after the alias name in angle brackets `< >`: ```kotlin typealias Predicate<T> = (T) -> Boolean typealias Transform<A, B> = (A) -> B typealias Reducer<S, A> = (S, A) -> S ``` Using `Predicate<String>` substitutes `T = String`, yielding exactly `(String) -> Boolean`: ```kotlin val isBlank: Predicate<String> = { it.isBlank() } fun <T> none(items: List<T>, p: Predicate<T>): Boolean = items.none(p) ``` ## How substitution + expansion works The compiler performs two conceptual steps at every use site: 1. **Substitute** the supplied type argument(s) into the right-hand side (`T` → `String`). 2. **Expand** the alias to the underlying type (`(String) -> Boolean`). The result is a normal function type — `kotlin.Function1<String, Boolean>` under the hood. So `Predicate<String>` and `(String) -> Boolean` are the **same** type and interchangeable. ## Bounds Type parameters of an alias can carry **upper bounds** just like generic classes: ```kotlin typealias NumberFold<T> = (T, T) -> T // unbounded typealias Adder<T : Number> = (T, T) -> T // bounded ``` The bound constrains what you may pass as the argument (`Adder<String>` would be rejected because `String` is not a `Number`). ## Variance A type alias itself does **not** declare variance (no `out`/`in` on its own parameters). Variance and subtyping are governed by the **expanded** function type. Recall function types are contravariant in parameters and covariant in return, so `Predicate<Any>` is assignable to a `Predicate<String>` reference in the usual function-type subtyping sense (a predicate over `Any` can test a `String`). ## Still just an alias No new generic class is generated. There is no `Predicate` type at runtime; `Predicate<Int>` erases to `Function1<Integer, Boolean>`. Two different aliases that expand to the same function type are not distinguishable — for that you need a real generic class or `fun interface`.
- Can a typealias type parameter have an upper bound?Yes: `typealias Adder<T : Number> = (T, T) -> T`. Passing a non-Number argument is rejected at compile time.
- Does `typealias Predicate<T>` declare `out`/`in` variance for T?No. Aliases don't declare variance; subtyping follows the expanded function type's own variance rules.
A fill-in-the-blank template: Predicate<__> is the form, and the type argument fills the blank to produce the real signature.
saying these in an interview costs you the question
- Believing the alias creates a real generic class at runtime
- Claiming you can put `out`/`in` variance modifiers on alias type parameters
- Thinking `Predicate<String>` differs in type from `(String) -> Boolean`
- Forgetting bounds are allowed and saying alias params are always unbounded
- Confusing alias expansion order (substitute then expand)