In this demo, you’ll make MuseumObject conform to the Comparable interface from the Kotlin standard library. Then, you’ll create an interface and a subclass of MuseumObject that adopts it.
Open the starter project for this lesson in Android Studio or continue with your project from the previous lesson. You won’t change any code in PublicDomainObject, MuseumObjectComposable WebViewComposable, so fold their code out of the way.
Start off by making MuseumObject implement the Comparable interface.
open class MuseumObject(
//...
): Comparable<MuseumObject> {
//...
}
The Comparable interface is a generic type, which means its exact type isn’t specified, so it can be of any type. In this case, you specify its type to be a MuseumObject.
You can also see you have an error, and if you hover over it, you can see it says MuseumObject is not an abstract class, so it must implement the compareTo() method of the interface.
Hold down the CMD key then click the Comparable interface. This takes you to its definition, and you can see the compareTo method doesn’t have a body, so you must implement it in MuseumObject. Close this file.
Hover over the error once again. Then click the “Implement members” suggestion from the popup. compareTo() is selected by default because it’s the only method to override. Go ahead and tap “OK.” Instantly, Android Studio adds the method signature to MuseumObject.
Replace the TODO in the body with the following code:
// return when {
// objectID > other.objectID -> 1
// objectID == other.objectID -> 0
// else -> -1
// }
return objectID.compareTo(other.objectID)
The commented out code is just the manual way to do the comparison. You use that approach when comparing for multiple criteria. Since you’re comparing for a single value which is the objectID, you use the compareTo itself for simplicity.
Scroll down to where you created instances for the MuseumObject and PublicDomainObject from the previous lesson. These instances are of the same art object.
val obj =
MuseumObject(
objectID = 436535,
title = "Wheat Field with Cypresses",
objectURL = "https://www.metmuseum.org/art/collection/search/436535",
// primaryImageSmall = "https://images.metmuseum.org/CRDImages/ep/original/DT1567.jpg",
creditLine = "Purchase, The Annenberg Foundation Gift, 1993",
isPublicDomain = true
)
val obj_pd =
PublicDomainObject(
objectID = 436535,
title = "Wheat Field with Cypresses",
objectURL = "https://www.metmuseum.org/art/collection/search/436535",
primaryImageSmall = "https://images.metmuseum.org/CRDImages/ep/original/DT1567.jpg",
creditLine = "Purchase, The Annenberg Foundation Gift, 1993",
)
For the example you’re about to see, assume the objectID here is like an incremental serial number of the art object. It’s based on the order or time in which the art object was added to the museum.
Now, scroll down once again to the MainActivity.
Then add in the following code to compare if one object is greater than the other:
println("COMPARE ${obj > obj_pd}")
Run your app.
Open up the logcat window. Search for “compare” to filter the log.
You can see it prints false because the two objects have the same ID, so no one is greater than the other.
Note: Behind the scenes, Kotlin actually compares the returned value of the
compareTomethod with the possible values, which are zero, a negative or a positive number.
To make sure the comparison isn’t just returning false all the time, the starter Android Studio Project also has a second MuseumObject. If you’re continuing with your project from the previous lesson, copy the “Afternoon among the Cypress” object from the transcript below this video to create obj2:
val obj2 =
MuseumObject(
objectID = 11521,
title = "Afternoon among the Cypress",
objectURL = "https://www.metmuseum.org/art/collection/search/11521",
creditLine = "Gift of Mrs. Henrietta Zeile, 1909",
isPublicDomain = false
)
Then compare once again:
println("COMPARE 2 ${obj > obj2}")
Run your app.
Open up the logcat window once again. You can see it returns true this time around because the objectID of obj is greater, meaning that it was added to the museum after the obj2’s art object.
This is just a basic comparison to show how the Comparable interface can be used and how you based your implementation on the objectID.
You can also define your own interfaces, to fine-tune your data model.
For example, an art object can be on display in the museum. Not all are. Many art objects are in storage or undergoing maintenance. An art object that is on display has a non-empty string for its galleryNumber property. Its web page has a link to show its location on a map of the museum.
You can use the interactive map to search and get directions from different points in the museum to the gallery.
Call showImage() with obj2:
obj2.showImage()
Run the project and wait for the web page to appear.
Scroll down to see the “On view” line. If you’re running this on a web browser outside the app, then clicking the Gallery 744 link opens an interactive map. It doesn’t work in the in-app browser.
Searching for directions into art objects on display is a nice bit of functionality built into the website. How would you model that in your app? You’ve probably guessed that you’re not going to create an OnDisplay subclass of MuseumObject. Where would that leave PublicDomainObject? Instead, you’re going to create an OnDisplay interface.
Add in the following code below the Create OnDisplay interface TODO:
interface OnDisplay {
val galleryNumber: String
fun showMap(from: String, to: String)
}
A data type that wants to implement OnDisplay must have a read-only property named galleryNumber and a method with the showMap signature.
Next, you need to create an OnDisplayObject subclass of MuseumObject. This is like PublicDomainObject, but it will have galleryNumber instead of primaryImageSmall in its constructor.
So scroll up to the PublicDomainObject. Copy its definition. Scroll back to the interface declaration. Then paste it below.
Next, update the name to OnDisplayObject, override the galleryNumber property and make it implement the OnDisplay interface like so:
class OnDisplayObject(
objectID: Int,
title: String,
objectURL: String,
creditLine: String,
isPublicDomain: Boolean = true,
override val galleryNumber: String, // Overriden property
) : MuseumObject(objectID, title, objectURL, creditLine, isPublicDomain), OnDisplay {
}
OnDisplay is added after the MuseumObject parent class. With this, OnDisplayObject has MuseumObject as it parent and it also abides to the contract specified in the OnDisplay interface.
You can also override the galleryNumber inside the class body, but since you’ll be getting it when creating the object, you do the overriding inside the constructor.
Alright, now you can see you still have an error on the class. If you hover over it, it says this class does not implement the showMap method. Remember, interfaces are contracts, and all non-abstract classes adopting it must implement all its members.
Override it now, add a showMap method stub inside the class like so:
override fun showMap(from: String, to: String) {
galleryNumber.takeIf { it.isNotEmpty() } ?: return
// implementation here
}
You simply check that the instance’s galleryNumber isn’t an empty string, and if it is empty, you exit the function. This is just a placeholder and you can code the actual implementation in the body.
With this, OnDisplayObject inherits from the MuseumObject class and conforms to the contract laid out by the OnDisplay interface.
Next, create an OnDisplayObject instance.
You can copy and paste it from the transcript below:
val obj_od =
OnDisplayObject(
objectID = 436535,
title = "Wheat Field with Cypresses",
objectURL = "https://www.metmuseum.org/art/collection/search/436535",
creditLine = "Purchase, The Annenberg Foundation Gift, 1993",
galleryNumber = "822"
)
Inside the MainActivity, call showImage() with obj_od and comment out the other call:
obj_od.showImage()
Run your app.
OnDisplayObject is a subclass of MuseumObject, so it inherits the parent class’s showImage(), which displays the webpage of the art object.
That ends this demo. Continue with the lesson for a summary.