Alright so we've now created the
layouts that we'll need to add new tasks
and display them in a list. Now it's time
to write the code that makes it happen.
One common approach when dealing with
records from a database, or from anywhere
really, is to create a simple data class
to store the fields. So I'm going to
start by doing something like that, so
we're going to click on our package, New go in
to select Kotlin/File class. I'm going
to type in the name Task - we'll call it
Task. I'm going to use the drop-down and
choose class rather than file, and press
Enter,
and then I'm going to add the word data
before the class definition. So data
class Task, and I'll talk about what that
means in a short while. Now we're going
to want to pass instances of this class
around in bundles, so for that reason we
need to make it serializable or parcelable.
Now if you remember we did this
with our basic photo class in the Flickr
browser app, and we talked about these
interfaces then. Now since we recorded
that video, Kotlin has improved even
further. So it now directly supports
implementing the parcelable interface
for us, and now it's so easy to do that
serializable probably won't be used in
Kotlin from now on. It's still there if
you get performance problems with
parcelable, but when you see just how easy
implementing parcelable is, I suspect that
most developers won't even consider
serializable. So what we want here is a
basic data class that implements the
parcelable interface. Alright so let's go
ahead and set this up for our tasks. So
on the class definition line I'm going
to adds parentheses var id: Long val
name: String, val description:
String and then val sortOrder: Int
capital I. And then we're implementing
Parcelable as I mentioned, so
colon Parcelable, and you can see there
Android Studio automatically takes care of
importing android.os.parcelable.
Alright so we've seen that before, and
the next step then was to implement all
the methods of the Parcelable
interface. But now things have changed so we
just need a simple
annotation. I want to go ahead and add
that annotation, so it's Parcelize, P a r c e l i z e,
and of course it's an @ at the start
of it because it's an annotation. Now I'm
recording this video using version 1.2
point 50 of Kotlin, and you saw a
few videos ago that I actually updated
to that version. And you can check what
version that you're actually
running by coming down to our build
scripts. And let me open the project Task
Timer and you can see that at the moment I'm
running 1.2.50.
Now in this version, the Parcelize
annotation is still experimental. And
as a result if we have a look at our
class definition for Task, we've still
got an error that our Task class doesn't
implement the required interface methods -
you can see that's coming up there. We've
also got another error, though, up here
and it says that Parcelize is an
unresolved reference. Now I'm showing you
this annotation because it's going to
make producing Parcelable classes
much easier. JetBrains are working on it
and I'd be very surprised if they don't
release it very soon. Right at the moment
though, it's experimental. Now if it
doesn't make it into a release version
for some reason, we've covered how to
implement Parcelable in detail in previous
videos. You'll need to implement the
method yourself, but you've got
everything you need to do that. But this
is obviously much easier and may even
work without enabling experimental
features. So up here on line 9, if
Parcelize isn't giving you errors
when you come to watch this video, then
there's no need to enable the
experimental features in Android Studio
that I'm about to do. At the moment
though, because I've got an error there,
we do need to enable experimental
features. So to do that we need to open
up our other Gradle file, the Module
app one. We need to scroll up to the top of
the file, under the three apply plugins
here, and I'm going to type in android
Extensions with a capital E there, left and
right curly braces and
within it I'm going to type experimental
equals true. And we sync the project and
that error should disappear, but if not
there's one other thing we may have to do
to get that error to disappear so we
can actually use the Parcelize
annotation. Alright so we'll just wait
for it to finish off.
That's finished now so I'm going to close
off this window, go back to our Task
class, and we've noticed we've still got
an error here and I'll just ignore the
IDE error that popped up. We've still got the
same error there about Unresolved
reference Parcelize. So you can see in
this case, even though I said the errors
should've gone it's still showing for
me. And that's because there's a bug in
the lint checker and at the moment, for
that reason, I also have to suppress the
check. So I'm going to just type in here,
on the next line up to Parcelize, the
next annotation will be the SuppressLint.
So @SuppressLint, then in parentheses
I'm going to type, in double quotes Parcel
Creator, and once I type that you can see
that Parcelize annotation is no longer
shown as an unresolved reference. And
there is actually an open issue for this
at the JetBrains issue tracker. So I'm
just going to quickly bring that up just to
show you that it is actually a bug at
the moment.
So you can see there that Parcelable
editor shows warning about
incomplete implementation on a class with
Parcelize annotation. That's something that
JetBrains are clearly going to be
working on at the moment. I'll just go
back to Android studio. So we thought at
the time that seemed to have been fixed.
I'm mentioning it just in case it
reappears when you come to watch this
video. So it may or may not though, but just
to be clear only add this SuppressLint
line, so in other words only suppress
the check, if you've still got an error
after enabling the experimental features.
And for that matter, only enable the
experimental feature if you got the
error in the first place.
Alright, so at this point though, that's
our class. Our data class is now Parcelable.
Now you might be wondering there, looking
at these four properties that we've
defined, why three of the properties were
set to val, or perhaps why the ID
property is set to var instead. Now
instances of this class will reflect a
row in the database. So if we allow the
properties to be changed, we could end up
with an object having different values
to what's stored in the corresponding
database row. And to prevent that from
happening
we're not going to allow these fields to
be changed. So if the values aren't going
to be changed, then it makes sense to
declare them as val so that they
actually can't be, and it also acts as
documentation that the behavior is
deliberate. But with that said, why isn't
the ID property a val? Well if you think
about it, when we save a new task we
don't know what the ID is until after
it's been saved to the database. If you
remember from when we were testing the
ContentProvider, we get the ID returned
when we call the insert function to save
the record. So we do need to update the
ID field for a new record when the
database tells us what ID it's used. So
that'll make more sense when you see the
records being saved in our code later. So
the last thing to discuss here, though,
about that our Task class is what a data
class is. And I've got a link I'm just
going to bring up on the screen and it's
actually from the Kotlin language
reference, and it's actually quite
informative. So basically a data class is
intended just to hold data. When you
create a data class you get some useful
functions created automatically.
So you can see you've got an equals function
there, used to compared two instances of
the class to check if they're equal or
not. There's also a toString there that
includes all the properties in the
constructor, and these componentN
functions can be useful. They provide a
way to de-structure an object into
individual variables. Now that's
something else you get for free with a
data class, and you can follow this link
to componentN functions if you don't
know what de-structuring is and want to
learn more about it. But I'll just go
back to that main page we're at. So
scrolling down though, this section here,
Properties Declared in the class body,
this section's also worth a read. The
functions that generated automatically,
equals, toString hashCode and copy, will
only use properties that we declare in
the primary constructor. Now that can be
useful in some cases, such as comparing a
Task that we've just created with one
read from the database. So if we create a
new task object from data entered by the
user, it won't have a valid ID. The data
read from the database will have an ID.
So that means that a comparison for
equality will fail because the ID
properties won't match.
So if comparisons like that are something
you need to do, then you just move the ID
into the class body. So let's go back to
Android Studio. So I'm going to change
this. I'm going to actually take out the
definition for the ID and the comma as
well, and I'm going to put that into the body there
with the class body, and I'm going to set
the value of to that to be equal to zero to
initialize it. So now the ID property
won't be included in the equals test, and
also won't be included for the other
auto-generated functions. So going back
to the definition, or the documentation
rather, for our data class.
There's a definition here of the copy
function, or description, I should say, for
the copy function, and one use for that
could be to make a copy of the task
before allowing the user to edit the
details. You could then check if the
editor task equals the original, and only
save the task back to the database if
they're different.
So that's Kotlin data classes. They're
basically just a simple ordinary class
with some extra functions added for us,
and they can be very useful when encapsul
ating data such as from a database. If
you want to do things like comparing
different instances of the class, or copy
instances, then certainly consider
creating a data class. But with all that
said, I'm not going to use one in our app
though. In fact that's why I've included
this discussion of them here. Kotlin
provides them and they can indeed be
very useful, but generally we don't get
anything for free and there's a downside
to data classes. If you don't intend
testing for equality, or copying them or
de-structuring them, then you've got a class
with all that extra overhead that you
are not going to use. Now looking at
the URL for this website up here, in the
top left hand corner,
it's kotlinlang.org. So this is
documentation about the Kotlin language
and isn't specific to Android. On Android
there's a limit to the number of methods
that your app can contain. Now the limit's
64k and if your app has more than 64,000
methods, it has to use something called
multi decks. So that makes the app large
and more complicated.
Now this Kotlin documentation doesn't
really consider things like that, and
that's because it's all about the
language and not specific to Android. Now
you'll often come across recommendations
to use data classes in Kotlin and I'm
not saying that you shouldn't use them,
but it really only makes sense if you're
going to make use of the functions they
include. If you're not going to need
those functions in your app, then just use
a simple class instead. And there's a
simple discussion of this that I
recommend you read, so let's just go to
that URL. So I'm not going to go into
detail about that in this video now but
have a read of this at your leisure.
It's interesting that each data class
you create may increase the method count
by 18, so keep that in mind as well. Al
right so that's data classes - useful but
only when you use what they provide. So
I'm going to go back to our code in Android Studio,
and I'm actually going to remove the
data keyword here and set it back, just
to a basic class. Alright, so that's our
simple Task class created. We can now
move on and create the activity to
handle adding tasks. So I'm going to stop
the video here, and we'll do that in the
next one.