156

Recently I found that AndroidStudio reminds me to remove some class cast. I remember that in the old time, we have to cast the result of findViewById, but now it's not necessary.

The result of findViewById is still View, so i want to know why we don't need to cast the class?

I can't find any documents mentioned that, can anyone find any document?

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Eric Zhao
  • 1,704
  • 2
  • 12
  • 14
  • 7
    because now it's ` T findViewById(int id)` ? – Selvin Jul 04 '17 at 09:54
  • you need casting in case of any operation which is not there in View class ,like in case of ImageView ,If you want to use setImageResource,then you need to cast findViewById with ImageView – Gagan Jul 04 '17 at 10:10
  • But I feel a bit inconvenient to know the variable type in a glance if removed the "redundant" casting. – 林果皞 Mar 07 '18 at 10:12

6 Answers6

239

Starting with API 26, findViewById uses inference for its return type, so you no longer have to cast.

Old definition:

View findViewById(int id)

New definition:

<T extends View> T findViewById(int id)

So if your compileSdk is at least 26, it means that you can make use of this :)

Floern
  • 33,559
  • 24
  • 104
  • 119
Eduard B.
  • 6,735
  • 4
  • 27
  • 38
  • Thanks, and another question. i can't find sources for sdk26 in sdk manager, so where can i find this new definition please? – Eric Zhao Jul 05 '17 at 03:11
  • Unfortunately, the sources haven't been released yet, Android O still being under preview. When the final version will be released, sources will come out as well. – Eduard B. Jul 05 '17 at 06:19
  • 19
    If we remove the cast, our apps can still run on lower devices, right? – WSBT Nov 30 '17 at 06:42
  • 18
    @user1032613: Yes the apps can still work on lower devices without any problem. – Alireza Noorali Dec 12 '17 at 11:59
  • 1
    Will this throw an exception if it is the wrong type? – fobbymaster Feb 24 '18 at 00:32
  • 1
    As in if the view in the layout file is of a different type? Yes, of course, it would still be a `ClassCastException`. – Eduard B. Feb 24 '18 at 18:46
  • Why does code without a type cast work on old devices with sdk<26? I mean, Android framework on old devices still has the old definition without a cast: "View findViewById(int id)" – activity May 11 '20 at 17:11
  • 1
    @activity There's no generics at runtime; it's just compile-time syntactic sugar. – laalto Jul 29 '20 at 09:57
  • It doesn't work. I have min SDK version set to 26 and I get an error saying "Cannot resolve symbol 'findViewById'" when I do `findViewById(R.id.main)`. If I change it to `(LinearLayout)findViewById(R.id.main)` it works though. – Donald Duck Aug 08 '21 at 14:14
13

According to this article:

The following function relies on Java’s generics automatic type inference in order to eliminate a need for manual casting:

protected <T extends View> T findViewById(@IdRes int id) {
    return (T) getRootView().findViewById(id);
}
zeroDivider
  • 1,050
  • 13
  • 29
12

In older versions:

AutoCompleteTextView name = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView);

From Android Studio 3.0 with SDK 26:

AutoCompleteTextView name = findViewById(R.id.autoCompleteTextView);
zeroDivider
  • 1,050
  • 13
  • 29
Midhun
  • 178
  • 1
  • 10
3

Android 0, clean up casting

One of the things that google announce in IO 2017 is something that’s called ‘cast away’ :). Android developer don’t have to do a manual casting for findViewById(). For example the old way to get a text view using findViewById() would be something like this.

TextView txtDesc = (TextView) findViewById(R.id.textViewDesc);
txtDesc.setText(getString(R.string.info_angkot_description));

While the new way would be like this

TextView txtDesc = findViewById(R.id.textViewDesc);
txtDesc.setText(getString(R.string.info_angkot_description));

It’s a simple change. But for a seasoned programmer, a clean code like this can make you very happy and it helps with your coding mood :)

To be able to do this you only needed to set your project compiled sdk version to version 26 in your app build.gradle.

You can still target earlier sdk version too, so it’s a non-intrusive changes.

Now the real problem, how do you clean that old code that uses casting all this time. Especially when you have like hundreds of activity files. You can do it manually, or maybe hired an intern to do it . But luckily for all of those intern, android studio already prepared to help us with this.

When you put your caret (or click on the redundant casting) android studio will suggest 2 option to handle the redundant casting.

First it will suggest to remove that redundant cast or you can select clean up code. It will remove all the redundant cast for that file. This is better, but we want more. We don’t want to open each file and do this clean up one by one.

One of the things that make IntelliJ idea Special is that a feature that is called intent action. All you have to do is push ctrl+shift+A and then type clean. And select Code Clean up action, and select the whole project scope. With this few simple steps your code will be a whole lot cleaner.

One important point is that you do this with some code versioning system. This way you can compare the changes that being made by the intent action and revert any files you want.

Copied from original post:

https://medium.com/@abangkis/android-0-clean-up-casting-c30acec56cef

daliaessam
  • 1,636
  • 2
  • 21
  • 43
  • 1
    the question was `why`, not `how`: `The result of findViewById is still View, so i want to know why we don't need to cast the class?` – zeroDivider Dec 24 '19 at 16:18
  • "All you have to do is push ctrl+shift+A and then type clean". What do you mean by "type clean"? If you start typing at that point, you'll erase the entire file – Stealth Rabbi Jan 14 '20 at 20:53
2

Android Studio reminds to remove casting, if you use common attributes from View class, like visibility or some common methods, like onClick()

For example:

((ImageView) findViewById(R.id.image_car)).setVisibility(View.VISIBLE);

In this case you can simply write:

findViewById(R.id.image_car).setVisibility(View.VISIBLE);
Tim
  • 176
  • 9
  • 2
    you still have to declare the type, you would have to write: findViewById(R.id.image_car).setVisibility(View.VISIBLE); – Slickelito Oct 18 '17 at 09:39
  • Android Studio reminds us to remove explicit casting because it's changed in implementation of Java’s generics automatic type inference - it doesn't have anything to do with what method you're using. – zeroDivider Apr 23 '19 at 22:17
0

In the source code of ViewGroup, there is a cast of the return argument. So there is no need to cast again:

@Nullable
public final <T extends View> T findViewById(@IdRes int id) {
    if (id == NO_ID) {
        return null;
    }
    return findViewTraversal(id);
}

@Override
protected <T extends View> T findViewTraversal(@IdRes int id) {
    if (id == mID) {
        return (T) this;  //###### cast to T
    }

    final View[] where = mChildren;
    final int len = mChildrenCount;

    for (int i = 0; i < len; i++) {
        View v = where[i];

        if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
            v = v.findViewById(id);

            if (v != null) {
                return (T) v; //###### cast to T
            }
        }
    }

    return null;
}
activity
  • 2,653
  • 3
  • 20
  • 44