Question
I am building an Android ListView in Kotlin and receive this compiler error:
Type inference failed: Not enough information to infer parameter T in
fun <T : View!> findViewById(id: Int): T!
Please specify it explicitly.
The error occurs when calling findViewById, such as when locating the ListView or the TextView in a row holder. How can I tell Kotlin which view type findViewById should return and fix the adapter code?
Short Answer
By the end of this page, you will understand why Android's findViewById uses a generic type in Kotlin, why Kotlin sometimes cannot infer that type, and how to specify it safely with <ViewType>, typed properties, or View Binding.
Concept
findViewById searches a view hierarchy using an integer resource ID, such as R.id.label. At runtime, Android knows which view is stored at that ID. At compile time, however, the ID itself does not tell Kotlin whether the result is a TextView, ListView, Button, or another View subclass.
In Kotlin, the Android method is represented conceptually as:
fun <T : View> findViewById(id: Int): T
T is a generic type parameter. Kotlin must determine a concrete type for T before it can compile the call.
For example, this explicitly tells Kotlin that the view is a TextView:
val label = row.findViewById<TextView>(R.id.label)
This matters because Kotlin can then check your code. If R.id.label actually points to a Button, the mistake is exposed at runtime when the layout is used, rather than allowing vague View values to flow through the program.
Older Android/Kotlin code commonly used an unchecked cast:
val label = row.findViewById(R.id.label) as TextView
Explicit generic types are clearer and avoid relying on a cast for type inference.
Mental Model
Think of findViewById as asking a building receptionist for the person assigned to room 12.
- The room number (
R.id.label) identifies where to look. - It does not describe the person's job.
- Kotlin asks you to say what kind of person you expect:
TextView,ListView, orButton.
Writing findViewById<TextView>(R.id.label) is like saying: “Please get the TextView from room label.” The ID finds the object; the generic type tells Kotlin how your code will use it.
Syntax and Examples
The general syntax is:
val view = root.findViewById<ViewType>(R.id.some_view_id)
For a ListView in an activity:
val listView = findViewById<ListView>(R.id.list)
listView.adapter = ListExampleAdapter(this)
For a TextView inside an inflated list-row layout:
class ListRowHolder(row: View) {
val label: TextView = row.findViewById<TextView>(R.id.label)
}
The type inside angle brackets is the expected view class. It must match the XML layout:
<TextView
android:id="@+id/label" />
Kotlin can also infer the type when a property has an explicit type:
val label: TextView = row.findViewById(R.id.label)
Using <TextView> is often easier to read in Android code because the expected type appears directly at the call site.
Step by Step Execution
Consider this row-holder code:
class ListRowHolder(row: View) {
val label = row.findViewById<TextView>(R.id.label)
}
Step by step:
- The adapter inflates
R.layout.list_row, producing a rootViewfor one row. - That root view is passed to
ListRowHolderasrow. R.id.labelidentifies a child view in that row layout.<TextView>supplies the generic typeTrequired byfindViewById.- Android locates the child view, and Kotlin treats the result as a
TextView. - The adapter can safely assign text:
holder.label.text = "Eins"
If the XML view for @id/label is not actually a TextView (or a subclass), the app will fail when that layout is loaded. Keep the Kotlin type and XML element consistent.
Real World Use Cases
Explicit findViewById types appear in many Android UI tasks:
- Activity setup: Find a
RecyclerView,ListView,Toolbar, or button aftersetContentView. - Custom views: Find child controls inside a reusable custom layout.
- Dialog layouts: Inflate a dialog and find its
EditTextor action buttons. - List and RecyclerView rows: Find row labels, images, and checkboxes when binding data.
- Legacy Android projects: Maintain screens written before View Binding was enabled.
Example for a dialog:
val dialogView = layoutInflater.inflate(R.layout.dialog_login, null)
val emailInput = dialogView.findViewById<EditText>(R.id.email_input)
val submitButton = dialogView.findViewById<Button>(R.id.submit_button)
Real Codebase Usage
In current Android projects, developers generally avoid repeated findViewById calls when possible by using View Binding. It creates type-safe properties based on layout XML.
However, when maintaining a legacy screen or a small custom view, findViewById is still common. Typical practices include:
- Make required root views non-null: use
row: View, notrow: View?. - Look up a row's child views once, then store them in a holder.
- Reuse the holder through
View.tagin aListViewadapter. - Use explicit generic types at lookup sites.
- Validate layout assumptions: an ID should refer to the same view type in every layout variant.
A conventional ListView row-reuse pattern is:
val holder = if (convertView == null) {
val row = inflater.inflate(R.layout.list_row, parent, false)
ListRowHolder(row).also { row.tag = it }
} else {
convertView.tag as ListRowHolder
}
The view lookup occurs only when a row is created, not every time a row is reused.
Common Mistakes
Calling a generic method without enough type context
This may fail in older Kotlin/Android API combinations:
val label = row.findViewById(R.id.label)
Fix it by supplying the type:
val label = row.findViewById<TextView>(R.id.label)
Using a nullable row when it should always exist
A holder represents an inflated row, so it normally should not accept View?:
// Avoid unnecessary nullability
class ListRowHolder(row: View?)
A nullable value forces safe calls and can hide a programming error. Prefer:
class ListRowHolder(row: View)
Casting after every lookup
This works but is less clear:
val label = row.findViewById(R.id.label) as TextView
Prefer:
val label = row.findViewById<TextView>(R.id.label)
Using the wrong type for the XML view
This compiles but fails when run if @id/label is an ImageView:
val label = row.findViewById<TextView>(R.id.label)
Check the corresponding XML layout and ensure the ID is attached to a TextView or subclass.
Forgetting setContentView
An activity must load its layout before searching it:
setContentView(R.layout.activity_main)
val listView = findViewById<ListView>(R.id.list)
Without the layout, the requested ID cannot be found in the activity's view hierarchy.
Comparisons
| Approach | Example | Best use | Notes |
|---|---|---|---|
| Explicit generic type | findViewById<TextView>(R.id.label) | Legacy code and custom views | Clear and type-aware. |
| Type from property | val label: TextView = findViewById(R.id.label) | Short local code | Kotlin uses the declared property type as context. |
| Unsafe cast | findViewById(R.id.label) as TextView | Older code you are maintaining | Works, but is less expressive and can obscure inference issues. |
| View Binding | binding.label | New Android layouts | Usually the preferred modern approach; generated properties are typed. |
findViewById and View Binding both depend on the XML layout being correct. View Binding reduces manual IDs and casts, while findViewById is useful when binding is unavailable or when writing reusable code around arbitrary view roots.
Cheat Sheet
// Find a view from an Activity, Fragment root, dialog, or row view
val text = root.findViewById<TextView>(R.id.label)
val button = root.findViewById<Button>(R.id.save_button)
val list = findViewById<ListView>(R.id.list)
findViewByIdis generic: its return type isT, whereTis aViewsubtype.- Use
<TextView>,<Button>,<ListView>, and so on when Kotlin cannot inferT. - The requested Kotlin type must match the view associated with that ID in XML.
- Call
setContentView(...)before looking up activity views. - Use
View, notView?, for a holder's required row view. - In new Android code, consider View Binding for generated, type-safe view references.
FAQ
Why does Kotlin say it cannot infer parameter T for findViewById?
findViewById is a generic method. Kotlin needs to know which View subtype it should return, but an ID integer alone may not provide enough compile-time information.
How do I explicitly specify the type for findViewById in Kotlin?
Put the expected view type in angle brackets:
val label = findViewById<TextView>(R.id.label)
Should I use as TextView or findViewById<TextView>?
Prefer findViewById<TextView>. It communicates the expected return type directly and avoids depending on a separate cast.
Can Kotlin infer the findViewById type from a property declaration?
Yes:
val label: TextView = findViewById(R.id.label)
Still, an explicit generic type is often clearer in Android UI code.
Why should ListRowHolder receive View instead of View??
A holder cannot work without a row view. Declaring it non-null makes that requirement explicit and prevents unnecessary safe calls such as row?.findViewById(...).
Does findViewById verify the XML type at compile time?
No. Kotlin knows the type you request, but Android layouts are loaded at runtime. If an ID points to the wrong view type, the error appears when that layout is used.
Is ListView still appropriate for new Android screens?
ListView remains available and is common in older apps. For many new, dynamic list screens, Android developers often choose RecyclerView, which has a more flexible adapter design.
Mini Project
Description
Build a small ListView screen that displays three translated number labels. The project demonstrates typed findViewById calls and the ViewHolder pattern used to reuse list-row views efficiently.
Goal
Create a working Kotlin BaseAdapter that finds a ListView and row TextView without type-inference errors.
Requirements
Create an activity layout containing a ListView with the ID list.
Create a row layout containing a TextView with the ID label.
Use findViewById<ListView> when obtaining the activity's list.
Use findViewById<TextView> in a row holder.
Display the values Eins, Zwei, and Drei.
Reuse an existing row through convertView when available.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.