Posts Tagged - programming

Herencia vs composicion

Ambas son dos maneras de reutilizar código para crear nuevas clases

Herencia

Permite que una subclase herede propiedades y métodos de su superclase o clase base. Facilita la reutilización de código y creación de relaciones jerárquicas entre clases.

Sin embargo puede llevar a estructuras de clases rígidas y complejas, y a problemas de acoplamiento.

Composición

En lugar de heredad de una clase base, la clase se “compone” de otras clases incluyendo instancias de otras clases como campos.

Esto promueve un diseño más modular y flexible ya que permite cambiar el comportamiento en tiempo de ejecución.

Depende del problema específico pero la composición suele ser preferida por su flexibilidad y capacidad para evitar problemas comunes de herencia.

Read More

Notepad++ log analysis

Delete all lines which doesn’t match a pattern

This pattern looks for everything that’s not [error] and deletes it from the file

^(?!.*\[error\]).*$

ctrl + F and replace everything with empty

See log files with colors

Go to C:\Users\your_user\AppData\Roaming\Notepad++\userDefineLangs. If the folder doesn’t exist, create it.

Create a file logSyntax.xml with the following content

<NotepadPlus>
    <UserLang name="logSyntax" ext="log" udlVersion="2.1">
        <Settings>
            <Global caseIgnored="yes" allowFoldOfComments="no" foldCompact="no" forcePureLC="0" decimalSeparator="0" />
            <Prefix Keywords1="yes" Keywords2="no" Keywords3="yes" Keywords4="yes" Keywords5="yes" Keywords6="no" Keywords7="no" Keywords8="no" />
        </Settings>
        <KeywordLists>
            <Keywords name="Comments">00WARN 01 02 03 04</Keywords>
            <Keywords name="Numbers, prefix1"></Keywords>
            <Keywords name="Numbers, prefix2"></Keywords>
            <Keywords name="Numbers, extras1"></Keywords>
            <Keywords name="Numbers, extras2"></Keywords>
            <Keywords name="Numbers, suffix1"></Keywords>
            <Keywords name="Numbers, suffix2"></Keywords>
            <Keywords name="Numbers, range"></Keywords>
            <Keywords name="Operators1"></Keywords>
            <Keywords name="Operators2"></Keywords>
            <Keywords name="Folders in code1, open"></Keywords>
            <Keywords name="Folders in code1, middle"></Keywords>
            <Keywords name="Folders in code1, close"></Keywords>
            <Keywords name="Folders in code2, open"></Keywords>
            <Keywords name="Folders in code2, middle"></Keywords>
            <Keywords name="Folders in code2, close"></Keywords>
            <Keywords name="Folders in comment, open"></Keywords>
            <Keywords name="Folders in comment, middle"></Keywords>
            <Keywords name="Folders in comment, close"></Keywords>
            <Keywords name="Keywords1"></Keywords>
            <Keywords name="Keywords2"></Keywords>
            <Keywords name="Keywords3"></Keywords>
            <Keywords name="Keywords4"></Keywords>
            <Keywords name="Keywords5"></Keywords>
            <Keywords name="Keywords6"></Keywords>
            <Keywords name="Keywords7"></Keywords>
            <Keywords name="Keywords8"></Keywords>
            <Keywords name="Delimiters">00 01 02 03INFO 04 05202 06DEBUG 07 08202 09ERROR 10 11202 12TRACE 13 14202 15 16 17 18 19 20 21 22 23</Keywords>
        </KeywordLists>
        <Styles>
            <WordsStyle name="DEFAULT" fgColor="FFFFFF" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="0" />
            <WordsStyle name="COMMENTS" fgColor="0080FF" bgColor="FFFFFF" fontStyle="7" nesting="0" />
            <WordsStyle name="LINE COMMENTS" fgColor="FFFF00" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="0" />
            <WordsStyle name="NUMBERS" fgColor="B2C57F" bgColor="FFFFFF" colorStyle="1" fontStyle="3" nesting="0" />
            <WordsStyle name="KEYWORDS1" fgColor="00CA28" bgColor="FFFFFF" colorStyle="1" fontStyle="3" nesting="0" />
            <WordsStyle name="KEYWORDS2" fgColor="0080FF" bgColor="FFFFFF" colorStyle="1" fontStyle="3" nesting="0" />
            <WordsStyle name="KEYWORDS3" fgColor="8000FF" bgColor="FFFFFF" colorStyle="1" fontStyle="3" nesting="0" />
            <WordsStyle name="KEYWORDS4" fgColor="FF0000" bgColor="FFFFFF" fontStyle="3" nesting="0" />
            <WordsStyle name="KEYWORDS5" fgColor="FF0080" bgColor="FFFFFF" colorStyle="1" fontStyle="3" nesting="0" />
            <WordsStyle name="KEYWORDS6" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
            <WordsStyle name="KEYWORDS7" fgColor="FF0000" bgColor="FFFFFF" colorStyle="1" fontStyle="3" nesting="0" />
            <WordsStyle name="KEYWORDS8" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
            <WordsStyle name="OPERATORS" fgColor="FF0080" bgColor="FFFFFF" colorStyle="1" fontStyle="1" nesting="0" />
            <WordsStyle name="FOLDER IN CODE1" fgColor="00FF80" bgColor="FFFFFF" fontStyle="7" nesting="0" />
            <WordsStyle name="FOLDER IN CODE2" fgColor="0080FF" bgColor="FFFFFF" fontStyle="7" nesting="0" />
            <WordsStyle name="FOLDER IN COMMENT" fgColor="0080FF" bgColor="FFFFFF" fontStyle="7" nesting="0" />
            <WordsStyle name="DELIMITERS1" fgColor="FF8040" bgColor="FFFFFF" colorStyle="1" fontStyle="3" nesting="0" />
            <WordsStyle name="DELIMITERS2" fgColor="61D502" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="0" />
            <WordsStyle name="DELIMITERS3" fgColor="4FA7FF" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="0" />
            <WordsStyle name="DELIMITERS4" fgColor="FF0000" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="0" />
            <WordsStyle name="DELIMITERS5" fgColor="8080FF" bgColor="FFFFFF" colorStyle="1" fontStyle="0" nesting="0" />
            <WordsStyle name="DELIMITERS6" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
            <WordsStyle name="DELIMITERS7" fgColor="C0C0C0" bgColor="FFFFFF" colorStyle="1" fontStyle="1" nesting="0" />
            <WordsStyle name="DELIMITERS8" fgColor="000000" bgColor="FFFFFF" fontStyle="0" nesting="0" />
        </Styles>
    </UserLang>
</NotepadPlus>

Restart
Select it from Language

SOLID Principles

These principles establish practices that helps maintain and extend software as it grows.

S - single responsibility
O - open/closed
L - liskov substitution
I - interface segregation
D - dependency inversion

Single responsibility

A class should have only one job - it should have only one reason to change

Read More

Visual Studio (Code)

Shortcuts and QoL features

  • ctrl + T - search for a class. If you want to search for UserController.cs you can type UCont and it will find it
  • ctrl + shift + P - search inside Visual Studio options
  • ctrl + D - duplicate actual line
  • alt + ↑ / ↓ - move code lines

Auto-complete placeholders

  • prop - creates a new property for a class
  • ctor - creates a new constructor
  • cw - creates a new Console.WriteLine() statement
  • try - creates a try-catch statement

To create a new property, type prop, hit twice tab and it creates a property template which you can navegate and override

vstudio 2

Multi-caret and multi-cursor editing

(check this for more details)

For lines that are aligned

  • alt + mouse click - selects a block to edit
  • alt + shift + arrow - same with keyboard

For multiple places that are not aligned

  • ctrl + alt + mouse click - click where you want to add a caret
  • (select the word you want to match) alt + shift + ; - vstudio selects all locations that match selected text in the current document and you may edit them

    Debug in VsCode

    Watch a variable

    En la pestaña watch se puede introducir el nombre de una variable y al hacer debug mostrará siempre el valor de esta variable.

vstudio-4

Read More

i18n & l10n

i18n is just a nomenclature for Internationalization. i18n involves, among other things, the ability to display translated content. It prepares a digital product for localization by for example, separate the content into strings so they are ready to be translated and delivered.

The same goes for L10n. This is a nomenclature for Localization. L10n involves translating content, adapting graphics and finalizing the producto for each regional market.

Reference(s)

https://www.oneskyapp.com/blog/i18n-need-to-know-internationalization/

Android WorkManager

WorkManager is an Android background tasks management system which came with Jetpack. We use it to run postponed background work. It will run even if the app is closed.

WorkManager is not for tasks that need to be run in a background thread, but they don’t need to survive the process death. Use Kotlin’s coroutines instead. WorkManager is for periodic work requests or one time work requests that need to run even if the user closses the App or needs constraints such as make sure the device is charging or be sure it uses WIFI.
It’s possible to chain multiple Workers together to run one after another.

Implementation

Add this to your build.gradle file.

dependencies {
        def work_version = "2.3.4"
        implementation "androidx.work:work-runtime-ktx:$work_version"
}

One Time Work Requests

Create a worker class. This will be executed by the work manager instance when conditions are met.

class UploadWorker(context: Context, params: WorkerParameters): Worker(context, prams) {
        override fun doWork(): Result {
                try {
                        // add your work here
                        return Result.success()
                } catch(e: Exception) {
                        return result.failure()
                }
        }
}

Now set it up.

class MainActivity: AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                setOneTimeWorkRequest()
        }
        
        private fun setOneTimeWorkRequest() {
                val uploadRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java)
                        .build()
                WorkManager.getInstance(applicationContext)
                        .enqueue(uploadRequest)
        }
}

Get status updates

For each work request, WorkManager allows us to get a LiveData<WorkInfo>. By observing it, we can determine the current status.

class MainActivity: AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                setOneTimeWorkRequest()
        }
        
        // change this
        private fun setOneTimeWorkRequest() {
                val workManager = WorkManager.getInstance(applicationContext)
                val uploadRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java)
                        .build()
                workManager.enqueue(uploadRequest)
                workManager.getWorkInfoByIdLiveData(uploadRequest.id)
                        .observe(this, Observer {
                                // this is just a TextView to show it off
                                textView.text = it.state.name
                        })
        }
}

Set up constraints

Sometimes we want to set up constraints. For example, if we want to download a large video, this consumes too much battery and we want to do it only if the battery’s connected.

class MainActivity: AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                setOneTimeWorkRequest()
        }
        
        private fun setOneTimeWorkRequest() {
                val workManager = WorkManager.getInstance(applicationContext)
                // added this
                val constraints = Constraints.Builder()
                        .setRequiresCharging(true)
                        .setRequiredNetworkType(NetworkType.CONNECTED)
                        .build()
                
                val uploadRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java)
                        // added this
                        .setConstraints(constraints)
                        .build()
                workManager.enqueue(uploadRequest)
                workManager.getWorkInfoByIdLiveData(uploadRequest.id)
                        .observe(this, Observer {
                                // this is just a TextView to show it off
                                textView.text = it.state.name
                        })
        }
}

Pass data to workers

Sometimes we need to pass data to the workers. It’s also possible to send data from the Workers to the Activity.

class MainActivity: AppCompatActivity() {

        // added this
        companion object {
                const val KEY_COUNT_VALUE = "key_count"
        }

        override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
                setOneTimeWorkRequest()
        }
        
        private fun setOneTimeWorkRequest() {
                val workManager = WorkManager.getInstance(applicationContext)

                // added this
                val data = Data.Builder()
                        .putInt(KEY_COUNT_VALUE, 125)
                        .build()
                
                val constraints = Constraints.Builder()
                        .setRequiresCharging(true)
                        .setRequiredNetworkType(NetworkType.CONNECTED)
                        .build()
                
                val uploadRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java)
                        .setConstraints(constraints)
                        // added this
                        .setInputData(data)
                        .build()
                workManager.enqueue(uploadRequest)
                workManager.getWorkInfoByIdLiveData(uploadRequest.id)
                        .observe(this, Observer {
                                // this is just a TextView to show it off
                                textView.text = it.state.name
                        })
        }
}

We get it like this at our Worker.

class UploadWorker(context: Context, params: WorkerParameters): Worker(context, prams) {
        override fun doWork(): Result {
                try {
                        // 0 is the default value
                        val count = inputData.getInt(MainActivity.KEY_COUNT_VALUE, 0)
                        // add your work here
                        return Result.success()
                } catch(e: Exception) {
                        return result.failure()
                }
        }
}

Periodic work requests

For now, Android Jetpack has a minimum repeat time of 15 minutes. The only difference is we need use PeriodicWorkRequest class instead.

fun setPeriodicWorkRequest() {
        val periodicWorkRequest = PeriodicWorkRequest
                .Builder(DownloadingWorker::class.java, 16, TimeUnit.MINUTES)
                .build()
        WorkManager.getInstance(applicationContext).enqueue(periodicWorkRequest)
}

Android Hilt (dependency injection)

Hilt is an Android Jetpack wrapper library to make dependency injection with Dagger easier and more efficient.

We have the following third-party dependency. With Dagger2 we would need to create a @Module, inject it into a @Component and create code into main to .built() it.

class DataSource {
	fun getRemoteData() {
		...
	}
}

Still, all classes that are going to be injected need to be marked as follows with @Inject and an empty constructor().

class DataSource @Inject constructor()

All activities and their fragments need to be marked with @AndroidEntryPoint.

Implementation

Add this into your project’s root build.gradle file.

buildscript {
	dependencies {
		classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
	}
}

Then add this into your app’s build.gradle file.

apply plugin: 'dagger.hilt.android.plugin'

// check this is correctly set.
android {
	compileOptions {
		// since Hilt uses Java8 features, add this
		sourceCompatibility JavaVersion.VERSION_1_8
		targetCompatibility JavaVersion.VERSION_1_8
	}
}

dependencies {
	implementation "com.google.dagger:hilt-android:2.28-alpha"
	kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
}

Then we need to annotate the our Application class.

@HiltAndroidApp
class App: Application()

Now go to your AndroidManifest.xml and set this attribute android:name\=".App".

<manifest>
	<application
	 	...
		android:name\=".App">
	</application>
</manifest>

With Hilt we don’t need to explicitly create a @Component interface. It already has 7 such interfaces packed for us.

Delete the @Component class and use the following annotations instead.

Hilt Component Injector for
ApplicationComponent Application
ActivityRetainedComponent ViewModel
ActivityComponent Activity
FragmentComponent Fragment
ViewComponent View
ViewWithFragmentComponent View annotated with @WithFragmentBindings
ServiceComponent Service

When we use Dagger, we list all the modules belong to a Component, at the top of the Component. Now we include the component of a module at the top of the module instead.

@Module
@InstallIn(ApplicationComponent::class)
class DataModule {
	@Provides
	fun providesDataSource(): DataSource{
		return DataSource()
	}
}

Let’s switch to MainActivity. Remove the .inject part and add the new @AndroidEntryPoint annotation. This generates an individual Hilt component for each class marked with it.

@AndroidEntryPoint
class MainActivity: AppCompatActivity() {
	@Inject
	lateinit var dataSource: DataSource
	
	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_main)
		
		// we may now remove this
		// (application as App).dataComponent.inject(this)
		
		dataSource.getRemoteData()
	}
}

Remember to rebuild the project.

Android Unit Testing

When we test in Android, we test our ViewModel. They don’t contain any View code. This is great for testing and this way we don’t depend on Android.

Implementation

We ought to mock threads. If we don’t, we’d have to run the entire Android system and this is something we want to avoid.

Add the following to your build.gradle file. All testImplementation and androidTestImplementation won’t be included in your APK.

def arch_version = "2.1.0"

testImplementation 'junit:junit:4.13'
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.2.1"
androidTestImplementation 'androidx.test.ext:junit:1.1.2'

// room testing
androidTestImplementation "androidx.arch.core:core-testing:2.1.0"

// mockito
testImplementation "org.mockito:mockito-core:1.10.19"
	
// user interface testing
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

// test LiveData
testImplementation "androidx.arch.core:core-testing:$arch_version"

// more readable assertions
testImplementation "com.google.truth:truth:1.0.1"

Read More

Android Notifications

Most notification features need at least API 26 (Android 8) to work.

Implementation

We have to first create a NotificationChannel. Then we define a NotificationManager instance.

A NotificationChannel has an ID, a name and a description. It also has an importance level. This defines how to interrupt the user for any notification belonging to this channel.

We need to create the NotificationChannel before posting any notification.

class MainActivity: AppCompatActivity() {
	private val channelID = "es.codes.mario.template.channel1"
	private var notificationManager: NotificationManager? = null
	
	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_main)
		
		notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
		createNotificationChannel(channelID, "TemplateChannel", "oh no! this is the channel's description")
		
		button.setOnClickListener {
			displayNotification()
		}
	}
	
	private fun displayNotification() {
		val notificationId = 45
		val notification = NotificationCompat.Builder(this@MainActivity.channelID)
			.setContentTitle("Template title")
			.setContentText("This is a test notification")
			.setSmallIcon(android.R.drawable.ic_dialog_info)
			.setAutoCancel(true)
			.setPriority(NotificationCompat.PRIORITY_HIGH)
			.build()
			
		notificationManager?.notify(notificationId, notification)
	}
	
	private fun createNotificationChannel(id: String, name: String, description: String) {
		// has to be higher than Oreo
		if(Build.VERSION.SDK_INT >= Build.VERSION_CODES_O) {
			val importance = NotificationManager.IMPORTANCE_HIGH
			val channel = NotificationChannel(id, name, importance).apply {
				description = channelDescription		
			}
		}
		
		notificationManager?.createNotificationChannel(channel)
		}
	}
}

Read More

Android SQLite Experience

Before reading this post, check Room SQLite Framework for Android

I’ve had a lot of trouble to connect to my Android Studio emulator SQLite database to run queries. Stetho didn’t show my databases and amitshekhar just plain didn’t work for me.

The solution was to externally use ADB to connect to a rooted, non-google-play emulator, run a set of queries there and directly work with SQLite through Command Line Interface.

Troubles to connect to SQLite instance

ADB not found in system

ADB is installed with Android Studio. For windows, add the following link to your system environment variables.

C:\Users\{your_user}\AppData\Local\Android\Sdk\platform-tools

Log-out and log-in for it to take place. Then you should be able to open your CMD and run adb queries there.

adbd cannot run as root in production builds

This happens when you try to get sudo permissions in a Google Play emulator image. Just get into Android Studio AVD manager, create a new emulator and use an image which doesn’t have the Google Play icon.

ADB remount doesn’t work

One of the proposed solutions was to run this set of queries.

adb root
// this command failed
adb remount
sqlite3 data/data/your/app/path/database/your_database

which failed spectacularly.

avb_ops_user.cpp:217: ERROR: Error writing data.
avb_user_verity.c:205: ERROR: Error writing to partition 'vbmeta'
Skipping /system for remount
...
Operation not permitted
Can not mount overlayfs for partitions: No such file or directory
failed to remount partition dev:/dev/block/dm-3 mnt:/vendor: Read-only file system

This went nowhere. See solution below.

Connect to the emulator

Open CMD and run this set of queries to open a database connection.

adb shell
sqlite3 data/data/es.codes.mario.weighttracker/databases/entry_database

You should have an open connection now.
If this doesn’t work, try to run the following commands.

adb shell
su
mount -o rw,remount /system
sqlite3 data/data/es.codes.mario.weighttracker/databases/entry_database

SQLite Queries

I’m used to work with MySQL. This are queries which change for SQLite and are not guaranteed to be standard SQL.

.tables # list all tables
.schema tablename # describes the table

Kotlin Coroutines

Introduction

There are two types of multitasking methods to manage multiple concurrent processes. In one type the OS controls the swich bewteen processes. The other type is called cooperative multitasking, in which processes control their behaviour by themselves.

Coroutines are software components that create subroutines for coporative multitasking. A coroutine can be introduced as a sequence of well managed sub tasks. To some extend, a coroutine can be considered as a light weight thread. You can execute many coroutines in a single thread. A coroutine can also be swiched between threads, and can be suspended from one thread and resumed from another thread.

In kotlin there’s a coroutine library since 1.3, which avoids us the need for RxJava, AsyncTask, Executors, HandlerThreads and IntentServices. The coroutines API allows us to write asynchronous code in a sequential manner.

Importance of coroutines

For a phone with a 90hz screen, our app has only 11ms (1000 ms / 90 refresh per second) to perform tasks in the Android main Thread, in between screen refreshes. By default android main thread has a set of regular responsabilities such as:

  • always parse xml
  • inflate view components
  • draw them again and again for every refresh
  • attend click events

If we add more tasks to the main thread, if its execution time exceeds this super small time gap between two refreshes, the app will show performance errors. The Screen might freeze. As a result of this, we should always implement long running tasks async in a separated thread.

Coroutines vs Threads

Couroutines are NOT the same as Threads. We have Main or UI Thread. We also have on demand back ground worker threads, but threads are not equal to coroutines. Any of those threads can have many coroutines running in it, at the same time.

Basic Implementation

We need to implement both core and Android dependencies.

dependencies {
	implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-RC2"
	implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0-RC2"
}

Let’s start with CoroutineScope. This is an interface which provides the scope of the coroutine. We need to run the task in a background thread, so we provide Dispatchers.IO. launch{} is the coroutine builder.

btnDownload.setOnClickListener {
	CoroutineScope(Dispatchers.IO).launch {
		downloadData()
	}
}

The function downloadData() will execute inside our new coroutine.

Coroutines Scope

In an App we can launch hundreds coroutines running at the same time. By default, coroutines don’t help us to keep track of them or any work that’s being done by them. If we don’t manage them carefully, there can be leaks in memory. To solve this, we must start all coroutines within a scope. Using the scope, we can easily keep track of coroutines, cancel them and handle errors or exceptions. We do this through the CoroutineScope interface.

There’s another interface called GlobalScope (very rarely used) which is used to launch top-level coroutines, which are operating on the whole application lifetime.

Coroutines Dispatchers

Both of these scopes also acts as a reference to the coroutine context. (Dispatchers.IO). We can include the name of a Job instance, plus a Dispatcher as the context of the job. This describes the kind of thread where the coroutine should be run. It’s recommended to start coroutines using Main Thread and then switch to Background Threads.

We have:

  • Dispatchers.Main - coroutine will run in the Main Thread (also called UI Thread). There’s only one, so execute only lightweight tasks.
  • Dispatchers.IO - coroutine will run in a background thread from a shared pool of on-demand created threads. We use this to work with local database, networking or work with files.
  • Dispatchers.Default - for CPU intensive stasks such as sorting a large list.
  • Dispatchers.Unconfined - coroutine will run in the current thread, but if it’s suspended and resumed it will run on suspending function’s thread. This is used with GlobalScope. It’s not recommended for Android Development.

We may as well create our own executors and dispatchers. Most of the time, we should use Main and IO.

Coroutines Builders

The following snippet .launch{} is the coroutine builder. This are extension functions of coroutine scopes.
There are four main builders:

  • launch - this launches a new coroutine without blocking the current thread. It returns an instance of Job, which can be used as a reference to the coroutine or to cancel it. We use this for coroutines that doesn’t have any result as the return value.
  • async - it’s used to get a result value. It allows us to launch coroutines in parallel without blocking the current thread. This returns an instance of Deferred<T>. We need to invoke await() to get the value. This are the ones we use most in Android Development.
  • produce - Is for coroutines which produces a Stream of elements. Returns an instance of ReceiveChannel.
  • runblocking - Used mostly for testing. It will block the thread while the coroutine is executing. It returns a result of type T.

Switch the Thread of a Coroutine

In Android, we cannot directly call to a view component running in the UI Thread from a Background Thread. This will crash with a CalledFromWrongThreadException.

THIS WON’T WORK

btnDownload.setOnClickListener {
	CoroutineScope(Dispatchers.IO).launch {
		downloadData()
	}
}

// executed in a coroutine
private fun downloadData() {
	for (i in 1..20000) {
		// THIS WON'T WORK.
		textView.text = "Downloading user $i in ${Thread.currentThread().name}"
	}
}

Only the original thread that created a view hierarchy can touch its views, therefore we have to call views from the UI Thread. We add withContext() and suspend to the function’s args, as withContext is a suspending function. We cannot call a suspending function from a normal function.

// executed in a coroutine
private suspend fun downloadData() {
	withContext(Dispatchers.Main) {
		for (i in 1..20000) {
			textView.text = "Downloading user $i in ${Thread.currentThread().name}"
		}
	}
}

Suspending Functions

Whenever a coroutine is suspended, the current stack frame of ther function is copied and saved into memory. When the function resumes after completing its task, the stack frame is copied back from where it was saved and starts running again.

Other libraries such as room and retrofit, also provide suspending functions support.

These are some of those suspending functions: withContext, withTimeout, withTimeoutOrNull, join, delay. If we’re going to call them from our own functions, we have to mark our own functions as suspend fun. A suspending function can only be called from a coroutine block or from another suspending function. suspending means a function with a heavy long running task.

A suspending function doesn’t block a thread. It only suspends the coroutine itself. That thread is returned to the pool while the coroutine is waiting, and when the waiting is done the coroutine resumes on a free thread in the pool.

Async Builder and Await

Writing code to download data in parallel and combine them at the end is called parallel decomposition. To do it, we need to use async coroutine builder. This returns and instance of Deferred. We can use it by invoking its await function.

fun asyncTest() {
	CoroutineScope(Main).launch {
		val stock1 = async(IO) {getStock1()}
		val stock2 = async(IO) {getStock2()}
		val total = stock1.await() + stock2.await()
	}
}

private suspend fun getStock1() {
	// ...
}

Since this coroutine runs on the Main Thread, we could add a Toast message or interact with the view.

Unstructured Concurrency

Unstructured concurrency does not guarantee to complete all the tasks of the suspending function, before it returns. There’re situations we need to launch multiple coroutines concurrently in a suspending function and get some result returned from the function.

This is the wrong way to do it!.

class UserDataManager {
	suspend fun getTotalUserCount(): Int {
		var count = 0
		
		CoroutineScope(Dispatchers.IO).launch {
			delay(1000)
			count = 50
		}
		
		return count
	}
}

This will return 0 instead of 50, as it doesn’t wait for the async task. The child coroutines can be still running, even after the completion of the parent coroutine.

Structured Concurrency

Structured concurrency guarantees to complete all the work started by coroutines within the child scope before the return of the suspending function. It helps us to keep track of tasks we started and to cancel them when needed.

All previous problems can be solved using the suspending function coroutineScope. It allows us to create a child scope, within a given coroutine scope. This guarantees the completion of the tasks when the suspending function returns.

class UserDataManager {
	var count = 0
	lateinit var deferred: Deferred<Int>
	
	suspend fun getTotalUserCount(): Int {
		coroutineScope {
			launch {
				delay(1000)
				count = 50
			}
		}
	
		deferred = async(IO) {
			delay(3000)
			return@async 70
		}
		
		return count + deferred.await()
	}
}

Coroutines patterns with ViewModel and LiveData

For either ViewModelScope and LifeCycleScope we need to implement the following libraries.

def arch_version = '2.2.0-alpha04'
implementation 'androidx.lifecycle:lifecycle-extensions:$arch_version'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:$arch_version'

ViewModelScope

We have a ViewModel class. We define the coroutine scope we want to use and the method where we call it from.

In Android everytime a ViewModel is cleared from memory, just before cleaning it invokes its onCleared() method.

Some of the coroutines we launch in a ViewModel have the potential to run even after the ViewModel is cleared from memory. It might run until app termination. If this is not intended, we will end up leaking memory. To avoid this, we need to clear the coroutine.

class MainActivityViewModel: ViewModel() {

	// allows us to control all coroutines launched in this scope
	private val myJob = Job()
	private val myScope = CoroutineScope(Dispatchers.IO + myJob)

	fun getUserData() {
		myScope.launch {
			// write code. 
		}
	}

	override fun onCleared() {
		super.onCleared()
		// this is useful to cancel the coroutines for a couple of ViewModels
		myJob.cancel()
	}

}

If we have 20 ViewModels, there’s a better alternative with viewModelScope as this may be a wasting of time. This is bounded to ViewModel’s lifecycle. It was created to automatically handle cancellation when the ViewModel’s onClear() is called.

viewModelScope is a CoroutineScope tied to a ViewModel. It needs the ktx dependency in gradle.

Now, for the previous ViewModel, we can simply use viewModelScope instead of myScope. Clearing will be done automatically if the ViewModel is cleared.

class MainActivityViewModel: ViewModel() {

	fun getUserData() {
		viewModelScope.launch {
			// write code. 
		}
	}

}

LifeCycleScope

A lifecycleScope is defined for each Lifecycle Object. All coroutines launched in this scope are canceled when the Lifecycle is destroyed. You can access the CoroutineScope either via lifecycle.coroutineScope or lifecycleOwner.lifecycleScope. This scope is for objects with a lifecycle, such as Activities and Fragments.

If it’s in an Activity or a Fragment, all the coroutines will be canceled when the onDestroy() method is called.

class MainActivity: AppCompatActivity() {

	override fun onCreate(savedInstanceState: Bundle?) {
		// ...
		
		lifecycleScope.launch {
			// do whatever with coroutines. 
		}
	}

}

Sometimes we might need to suspend execution of a code block, considering the current state of a lifecycle object. For that we have three additional builders.

  • lifecycleScope.launchWhenCreated - if you have long running operations which should happen only once during the lifecycle of the activity or fragment. This will be called when it’s created for the first time.
  • lifecycleScope.launchWhenStarted - this will launch when the activity or fragment started. For some operations we need our Fragment lifecycle to be at least started.
  • lifecycleScope.launchWhenResumed - this is the state in which the App interacts with the user.

LiveData Builder

Example Without LiveData Builder

We have the following data class with two properties.

data class User(val id: Int, val name: String)

Then we create a repository class.

class UserRepository {
	suspend fun getUsers(): List<User> {
		// implement
	}
}

We also need a ViewModel class. with a viewModelScope which launches a new coroutine. Then we have a withContext() which switches the thread of the coroutine to a background thread.

class MainActivityViewModel: ViewModel() {
	private var userRepository = UserRespository()
	var users: MutableLiveData<List<User>> = MutableLiveData()
	
	fun getUsers() {
		viewModelScope.launch {
			var result: List<User>? = null
			withContext(Dispatchers.IO) {
				result = userReppository.getUsers()
			}
			users.value = result
		}
	}
}

This is the MainActivity class. We invoke the previous function and observe the list of users.

override fun onCreate(savedInstanceState: Bundle?) {
	...
	
	mainActivityViewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
	mainActivityViewModel.getUsers()
	mainActivityViewModel.users.observe(this, Observer { myUsers ->
		myUsers.forEach {
			Log.i("MyTag", "name is ${it.name}")
		}
	})
}

With LiveData Builder

To use this we need to add the following dependency.

def arch_version = '2.2.0-alpha04'
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$arch_version"

There’s a new block which will automatically execute when LiveData becomes active. It automatically decides when to stop executing and cancel the coroutines inside the building block considering the state of the lifecycle owner.

Inside the LiveData building block, you can use emit() function to set a value to LiveData.

This is the previous LiveModel. Remember long running tasks need to be executed in a background thread.

class MainActivityViewModel: ViewModel() {
	private var userRepository = UserRespository()
	
	var users = liveData(Distpatchers.IO) {
		val result = usersRepository.getUsers()
		emit(result)
	}
	
}

With this change, we don’t need to invoke the function in MainActivity anymore.

override fun onCreate(savedInstanceState: Bundle?) {
	...
	
	mainActivityViewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
	mainActivityViewModel.users.observe(this, Observer { myUsers ->
		myUsers.forEach {
			Log.i("MyTag", "name is ${it.name}")
		}
	})
}

Android Room Data Persistance (SQLite)

SQLite is the most used database for mobile Apps. Room Data Persistance is a library introduced in 2017 to help with this task. It uses annotations to generate boilerplate code.

When we use Room in a project, we need to create 3 types of Room specific classes.

  • A room @Database class, which represents the actual sqlite database.
  • An @Entity class, which represents the tables.
  • DAO interfaces for data access methods.

Implementation

Let’s modify our build.gradle and add kapt for annotations processor. We also add the dependencies for room, databinding, viewmodel, livedata and coroutines.

apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
	buildFeatures {  
		dataBinding = true  
	}
}

dependencies {
	def lifecycle_version = "2.2.0"
	def room_version = "2.2.3"
	
	// annotation processor
	kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
	
	// viewmodel
	implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
	
	// livedata
	implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
	
	// sqlite room dependency
	implementation "androidx.room:room-runtime:$room_version"
	kapt "androidx.room:room-compiler:$room_version"
	
	// coroutines support for room
	implementation "androidx.room:room-ktx:$room_version"
	
	// coroutines support in Android
	implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3"
	implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3"
}

Entity Classes

We’re going to create the following table subscriber_data_table. It contains three columns.

  • subscriber_id (primary key)
  • subscriber_name
  • subscriber_email

This is its class representation.

@Entity(tableName = "subscriber_data_table")
data class Subscriber(

	@PrimaryKey(autoGenerate = true)
	@ColumnInfo(name = "subscriber_id")
	val id: Int,
	
	@ColumnInfo(name = "subscriber_name")
	val name: String,
	
	@ColumnInfo(name = "subscriber_email")
	val email: String
	
)

Data Access Object (DAO) Interface

We have the following DAO class. The function’s name is not important. We only need the @Insert annotation. The method has suspend modifier because room doesn’t support database access on the main thread as it might lock the UI for a long time. The easiest way to solve this are kotlin coroutines.

For verification we might need to get a return value. We can get the new rowIds as the return value.

As parameters, OnConflictStrategy is an important one. REPLACE will try to insert the value, and replace it if an existing matchine one if found. On the other hand IGNORE will just ignore the new one.

With @Query annotation, room allows us to include a SQL query that would run when the function is called. This query will be verified at compile time.

Room facilitates us to get data from a database table as a LiveData of list of entities. These queries are called Async queries because for them, which have a LiveData as a return value, room always runs them on a background thread by itself, so we don’t have to write special code to use coroutines.

@Dao
interface SubscriberDAO {

	@Insert(onConflict = OnConflictStrategy.REPLACE)
	suspend fun insert(subscriber: Subscriber): Long

	@Insert
	suspend fun insert(subscribers: List<Subscriber>): List<Long>

	@Update
	suspend fun update(subscriber: Subscriber)
	
	@Update
	suspend fun update(subscribers: List<Subscriber>)
	
	@Delete
	suspend fun delete(subscriber: Subscriber)
	
	@Delete
	suspend fun delete(subscribers: List<Subscriber>)

	@Query("DELETE FROM subscriber_data_table")
	suspend fun deleteAll()

	// this isn't a suspend function!
	@Query("SELECT * FROM subscriber_data_table")
	fun getAllSubscribers(): LiveData<List<Subscriber>>

}

Database class

At @Database we need to provide the list of entity classes, then the database version number. This is important when we are migrating the database from one version to another. Then we need to declare an abstract reference for the DAO interface.

Usually we should only use one instance of a room database for the entire App. In Kotlin we create singletons as companion objects.

@Database(entities = [Subscriber::class], version = 1)
abstract class SubscriberDatabase: RoomDatabase() {

	abstract val subscriberDAO: SubscriberDAO

	companion object {
		@Volatile
		private var INSTANCE: SubscriberDatabase? = null
			fun getInstance(context: Context): SubscriberDatabase {
				synchronized(this) {
					var instance = INSTANCE
					if(instance==null) {
						instance = Room.databaseBuilder(
							context.applicationContext,
							SubscriberDatabase::class.java,
							"subscriber_data_database"
						).build()
					}
					return instance
				}
			}
	}

}

Repository in MVVM Architecture

Model in MVVM means all data management related components. It has local database related components, remote data sources related components and a repository.

The purpose of a repository class it to provide a clean API for ViewModels to easily get and send data. You can think of repositories as mediators between different data sources, such as local databases, web services and caches.

class SubscriberRepository(private val dao: SubscriberDao) {
	
	// this already is LiveData
	val subscribers = dao.getAllSubscribers()
	
	suspend fun insert(subscriber: Subscriber) {
		dao.insert(subscriber)
	}
	
	suspend fun update(subscriber: Subscriber) {
		dao.update(subscriber)
	}
	
	suspend fun delete(subscriber: Subscriber) {
		dao.delete(subscriber)
	}
	
	suspend fun deleteAll(subscriber: Subscriber) {
		dao.deleteAll(subscriber)
	}
}

ViewModel and DataBinding

We’re going to use DataBinding in a ViewModel with its related View. The view will contain two fields that will be updated to show the entity values and the view will contain EditText for those two values, and two buttons that will change dynamically.

class SubscriberViewModel(private val repository: SubscriberRepository): ViewModel(), Observable {
	
	val subscribers = repository.subscribers
	
	@Bindable
	val inputName = MutableLiveData<String>()
	
	@Bindable
	val inputEmail = MutableLiveData<String>()
	
	@Bindable
	val saveOrUpdateButtonText = MutableLiveData<String>()
	
	@Bindable
	val clearAllOrDeleteButtonText = MutableLiveData<String>()
	
	init {
		saveOrUpdateButtonText.value = "Save"
		clearAllOrDeleteButtonText.value = "Clear All"
	}
	
	fun saveOrUpdate() {
		val name = inputName.value!!
		val email = inputEmail.value!!
		// since the ID is AutoGenerated we can just set 0 and it will be ignored
		insert(Subscriber(0, name, email))
		inputName.value = null
		inputEmail.value = null
	}
	
	fun clearAllOrDelete() {
		clearAll()
	}
	
	// this will be executed in a background thread
	fun insert(subscriber: Subscriber) = viewModelScope.launch {
		repository.insert(subscriber)
	}
	
	fun update(subscriber: Subscriber) = viewModelScope.launch {
		repository.update(subscriber)
	}
	
	fun delete(subscriber: Subscriber) = viewModelScope.launch {
		repository.delete(subscriber)
	}
	
	fun clearAll() = viewModelScope.launch {
		repository.deleteAll()
	}
	
	override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
		
	}
	
	override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
	
	}
	
}

This is the related view.

<layout>
	<data>
		<variable
			  name="myViewModel"
			  type="es.codes.mario.templates.SubscriberViewModel"/>
	</data>
	<LinearLayout>
		<EditText
			...
		    android:text="@={myViewModel.inputName}"/>
		<EditText
		  	...
		  	android:text="@={myViewModel.inputEmail}"/>
		<Button
			...
			android:text="@={myViewModel.saveOrUpdateButtonText}"
			android:onClick="@{()->myViewModel.saveOrUpdate()}"/>
		<Button
			...
			android:text="@={myViewModel.clearAllOrDeleteButtonText}"
			android:onClick="@{()->myViewModel.clearAllOrDelete()}"/>
		...
	</LinearLayout>
</layout>

Then we will create a Factory for our ViewModel.

class SubscriberViewModelFactory(private val repository: SubscriberRepository): ViewModelProvider.Factory {
	
	// this is boilerplate code for all Factories
	override fun <T: ViewModel?> create(modelClass: Class<T>): T {
		if(modelClass.isAssignableFrom(SubscriberViewModel::class.java)) {
			return SubscriberViewModel(repository) as T
		}
		throw IllegalArgumentException("Unknown View Model class")
	}
	
}

This is the code at Main that binds everything.

class MainActivity: AppCompatActivity() {
	
	private lateinit var binding: ActivityMainBinding
	private lateinit var subscriberViewModel: SubscriberViewModel
	
	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
		val dao = SubscriberDatabase.getInstance(application).subscriberDAO
		val repository = SubscriberRepository(dao)
		val factory = SubscriberViewModelFactory(repository)
		subscriberViewModel = ViewModelProvider(this, factory).get(SubscriberViewModel::class.java)
		binding.myViewModel = subscriberViewModel
		// this is for livedata usage with data binding
		binding.lifecycleOwner = this
		displaySubscribersList()
	}
	
	private fun displaySubscribersList() {
		subscriberViewModel.subscribers.observe(this, Observer {
			Log.i("MyTAG", it.toString())
		})
	}
	
}

Displaying Toast with ViewModel and LiveData

This way we display messages through Toast when methods inside the ViewModel are executed. The ViewModel shouldn’t know any detail about the View.

Copy the following Event class as is.

open class Event<out T>(private val content: T) {
        var hasBeenHandled = false
                private set // allows external read but not write
                
        /**
         * Returns the content and prevents its use again
         */
        fun getContentIfNotHandled(): T? {
                return if(hasBeenHandled) {
                        null
                } else {
                        hasBeenHandled = true
                        content
                }
        }
        
        /**
         * Returns the content, even if it's already been handled
         */
        fun peekContent(): T = content
}

Now do this inside ViewModel

class ViewModel {
        // this is MutableLiveData so we can edit its value, but it's private so we only can access it from this class. 
        private val statusMessage = MutableLiveData<Event<String>>()
        
        val message: LiveData<Event<String>>
                get() = statusMessage
                
        fun insert() {
                ...
                statusMessage.value = Event("subscriber has been inserted")
        }
        
        fun delete() {
                ...
                statusMessage.value = Event("subscriber has been deleted")
        }
}

You use it like this, at the class you’re going to use your ViewModel at.

~~~ kotlin class MainActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { val viewModel = …

            ...
            
            viewModel.message.observe(this, Observer {
                    it.getContentIfNotHandled()?.let {
                            Toast.makeText(this, it, Toast.LENGTH_LONG).show()
                    }
            })
    } }

Android Recycler View

The old way to display data was ListViews and GridViews, but they were very much memory inefficient. RecyclerView is a much more memory efficient, more advanced version.

RecyclerView without Data Binding (Kotlin Synthetics)

Go to activity_main.xml and put a new RecyclerView into the top ConstraintLayout. For the example, we changed its id to my_recycler_view.

Now go to MainActivity. This way of using directly the ID is called Kotlin synthetics. It’s useful to have concise code for smaller projects, instead of using findViewById or Data Binding. This is not recommended for larger, complex projects. Use Data Binding instead.

Set a layout manager for our recycler view. There’re 3 of them (we may create our own custome layout managers)

  • LinearLayoutManager - standard listviews
  • GridLayoutManager - display items on a grid
  • StaggeredGridLayoutManager - position list items in a saggered grid

Then we set an adapter instance for the recycler view. An adapter class should be a subclass of RecyclerView library’s adapter class. We will set our MyRecyclerViewAdapter as the adapter of the RecyclerView.

class MainActivity: AppCompatActivity() {
	val fruitsList = listOf(Fruit("Mango", "Tom"), Fruit("Apple", "Mario"), Fruit("Banana", "Carlos"))
	
	override fun onCreate(savedInsanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_main)
		
		my_recycler_view.setBackgroundColor(Color.YELLOW)
		// this 2 lines here
		my_recycler_view.layoutManager = LinearLayoutManager(this)
		my_recycler_view.adapter = MyRecyclerViewAdapter(fruitsList)
	}
}

In most practical situations we need to send a list of objects to the adapter and display their values in the recycler view. To do this we need a Data class.

data class Fruit(val name: String, val supplier: String)

This is our customly created Adapter for our ViewHolder.
An adapter class has three main functions which we need to implement.

Create the resource file we need right click -> new -> android resource file -> file name: list_item; resource type: layout -> OK. Convert it to a ConstraintLayout.

// this 2 classes go together in a single Kotlin file. 
class MyRecyclerViewAdaper(val fruitsList: List<Fruit>): RecyclerView.Adapter<MyViewHolder>() {

	// this is where we create the list item xml template.
	override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyviewHolder {
		// get layout inflator and using that, inflate the list item.
		val layoutInfater = LayoutInflater.from(parent.context)
		val listItem = layoutInflater.inflate(R.layout.list_item, parent, false)
		return MyViewHolder(listItem)
	}

	// returns the total number of items in the dataset held by the adapter. 
	// RecyclerView will create a number of spots for list items considering this value
	override fun getItemCoun(): Int {
		return fruitsList.size; // we will see 5 rows. 
	}

	// we use this to display data on the list item. 
	// the position param represents the position between 0 and getItemCount()
	override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
		val fruit = fruitsList[position]	
		holder.bind(fruit)
	} 

}

// we use this to receive data from onBindViewHolder()
class MyViewHolder(val view: View): RecyclerView.ViewHolder(view)  {

	fun bind(fruit: Fruit) {
		// name_text_view is the name of a TextView we use to show
		view.name_text_view.text = fruit.name
	}

}

Add a click listener to a Recycler View

We will use a lambda to abstract this function.
On MainActivity we define a new method which is a Fruit consumer. Then we pass it to MyRecyclerViewAdapter as a param arg, and from here to MyViewHolder where the click listener is defined.

class MainActivity: AppCompatActivity() {
	val fruitsList = listOf(Fruit("Mango", "Tom"), Fruit("Apple", "Mario"), Fruit("Banana", "Carlos"))
	
	override fun onCreate(savedInsanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(R.layout.activity_main)
		
		my_recycler_view.setBackgroundColor(Color.YELLOW)
		// this 2 lines here
		my_recycler_view.layoutManager = LinearLayoutManager(this)
		my_recycler_view.adapter = MyRecyclerViewAdapter(fruitsList, {
			selectedFruitItem: Fruit -> listItemClicked(selectedFruitItem)
		})
	}
	
	private fun listItemClicked(fruit: Fruit) {
		Toast.makeText(this@MainActivity,
			"Supplier name is ${fruit.supplier}",
			Toast.LENGTH_LONG).show()
	}
}
// this 2 classes go together in a single Kotlin file. 
class MyRecyclerViewAdaper(private val fruitsList: List<Fruit>, private val clickListener:(Fruit)->Unit): RecyclerView.Adapter<MyViewHolder>() {

	// this is where we create the list item xml template.
	override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyviewHolder {
		// get layout inflator and using that, inflate the list item.
		val layoutInfater = LayoutInflater.from(parent.context)
		val listItem = layoutInflater.inflate(R.layout.list_item, parent, false)
		return MyViewHolder(listItem)
	}

	// returns the total number of items in the dataset held by the adapter. 
	// RecyclerView will create a number of spots for list items considering this value
	override fun getItemCoun(): Int {
		return fruitsList.size; // we will see 5 rows. 
	}

	// we use this to display data on the list item. 
	// the position param represents the position between 0 and getItemCount()
	override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
		val fruit = fruitsList[position]	
		holder.bind(fruit, clickListener)
	} 

}

// we use this to receive data from onBindViewHolder()
class MyViewHolder(val view: View): RecyclerView.ViewHolder(view)  {

	fun bind(fruit: Fruit, clickLisener:(Fruit->Unit)) {
		// name_text_view is the name of a TextView we use to show
		view.name_text_view.text = fruit.name
		view.setOnClickListener {
			clickListener(fruit)
		}
	}

}

Android Navigation Architecture Component

The new trend in android dev is single activity, multiple fragments model. When we navigate, we usually create a single empty activity which acts as a basement. All other screens of the app will be created using fragments. This is the recommended best practice by Google android team.

There are three main parts of a navigation component.

  • Navigation graph - XML resource file that contains all navigation-related information. This allows us to manage navigation related tasks from a single location.
  • NavHostFragment - Empty container we keep on the activity to hold the navigation graph.
  • NavController - Class generated by the navigation library to manage the navigation between destinations we added to the navigation graph.

Dependencies

Add navigation dependencies, safeargs and data binding.

apply plugin: "androidx.navigation.safeargs.kotlin"

android {
	dataBinding {
		enabled = true
	}
}

dependencies {
	def nav_version = "2.1.0"
	
	implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
	implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}

Remember to add <layout> tags to the outer most tag in .xml files.
Add this classpath in your top level build.gradle.

dependencies {
	def nav_version = "2.1.0"
	
	classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}

(add photo of nav_graph)

It shows all the App’s screens and how the user can reach them, in a single location. They’re called destinations. It allows us to manage all the navigated related actions.

To create it, right click in the app folder > new > android resource file > file name: nav_graph; resource type: navigation. It will appear under a new folder navigation

When created, there’s no destination nor NavHostFragments yet.

To host the navigation graph, we need a host fragment. Navigation graph connects to other parts of the app through this host fragment.

To add a NavHostFragment, go to activity_main.xml, designer mode, select Containers, drag NavHostFragment. It automatically recognizes the navigation graph we created previously. Click on it. Click on infer constraints.

(add photo NavHostFragment1)

Under the hood, this is just a newly added fragment, with some special properties.

Now it should show the host on nav_graph.xml.

(add photo NavHostFragment2)

Different fragments in the nav graph are called navigation destinations. Click on *add a destination* > Create new destination > Fragment Name: HomeFragment > Accept.

This will:

  • Create a new HomeFragment.kt class.
  • Create a new fragment_home.xml layout resource.
  • Add it to nav_graph.xml
  • Add a placeholder to strings.xml

Inside nav_graph.xml it will contain a home symbol. This indicates start destination. If you doule click on the fragment’s representation, this takes you to its fragment_home.xml. Since ConstraintLayout is much easier to work with, replace the main Container with it. right click on FrameLayout > Convert FrameLayout to ConstraintLayout > OK. If you want to use DataBinding, remember to surround it by <layout> tags.

Add a new destination

In nav_graph.xml select the graph you want to be the base. Click on New Destination > Create new Destination > Fragment Name: SecondFragment; unclick options > OK.

Now we have two destinations.

We have two empty fragments: HomeFragment and SecondFragment. Go to nav_graph.xml. Select HomeFragment, hover the mouse over it and drag the circle it will appear to its SecondFragment. This creates an action with a given id action_homeFragment_to_secondFragment.

To execute this action on the click of a button, go to your HomeFragment (using DataBinding) and set the onClickListener action.

class HomeFragment: Fragment() {
	private lateinit var binding: FragmentHomeBinding
	
	override fun onCreateView(
		inflater: LayoutInflater, container: ViewGroup?,
		savedInstanceState: Bundle?
	): View? {
		binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
		// this here
		binding.button.setOnClickListener {
			it.findNavController().navigate(R.id.action_homeFragment_to_secondFragment)
		}
		return binding.root
	}
}

Transfer data between destinations

It’s not recommended to pass data beween destinations. The best practice is using a view model and get the data from the view model, but Android Navigation architecture component allows us to pass data.

In the HomeFragment we have an EditText with id editText to get the user input and a submit Button. In the SecondFragment we have a TextView. We will display the user input from HomeFragment into SecondFragment.

Starting point for HomeFragment.

class HomeFragment: Fragment() {
	private lateinit var binding: FragmentHomeBinding
	
	override fun onCreateView(
		inflater: LayoutInflater, container: ViewGroup?,
		savedInstanceState: Bundle?
	): View? {
		binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
		binding.button.setOnClickListener {
			it.findNavController().navigate(R.id.action_homeFragment_to_secondFragment)
		}
		return binding.root
	}
}

We get the user input as a bundle. Then we pass tha bundle as the second argument of this navigate function.

class HomeFragment: Fragment() {
	private lateinit var binding: FragmentHomeBinding
	
	override fun onCreateView(
		inflater: LayoutInflater, container: ViewGroup?,
		savedInstanceState: Bundle?
	): View? {
		binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
		binding.button.setOnClickListener {
			// this here. The if avoids possible null pointers.
			if(!TextUtils.isEmpty(binding.editText.text.toString())) {
				val bundle = bundleOf("user_input" to binding.editText.text.toString())
				it.findNavController().navigate(R.id.action_homeFragment_to_secondFragment, bundle)
			}
		}
		return binding.root
	}
}

Starting point for SecondFragment.

 class SecondFragment: Fragment() {
 	private lateini var binding: FragmentSecondBinding
	
	override fun onCreateView(
		inflater: LayoutInflater, container: ViewGroup?.
		savedInstanceState: Bundle?
	): View? {
		// inflate the layout for this fragment
		binding = DataBindingUtil.inflate(inflate, R.layout.fragment_second, conainer, false)
		return binding.root
	}
 }

We have added the bundle as an argument, so here we need to retrieve it.

 class SecondFragment: Fragment() {
 	private lateini var binding: FragmentSecondBinding
	
	override fun onCreateView(
		inflater: LayoutInflater, container: ViewGroup?.
		savedInstanceState: Bundle?
	): View? {
		// inflate the layout for this fragment
		binding = DataBindingUtil.inflate(inflate, R.layout.fragment_second, conainer, false)
		
		// this here
		var input: String? = arguments!!.getString("user_input"  )
		binding.textView.ext = input.toString()
		
		return binding.root
	}
 }

## Animations for Actions Go to navigation_graph.xml and select an action action_homeFragment_to_secondFragment. There’s a block Animations where you may select animations for Enter, Exit, Pop Enter, Pop Exit.
When we navigate from one destination to anoher, the new screen will appear with Enter animation, and the previous window will disspear with Exit animation. When the user clicks on the back button of he phone (the screen navigates backwards), the current screen will disappear with Pop Exit and the previous window will appear with the Pop Enter animation.

You may search for animations, such as this here.

Take one of them and create a resource file Right click on project > new > Android Resource File > File name: slide_in_left; Resource Type: Animation. Copy and paste the animation you want.

With the animations already created, select the one you want for the action.

ViewModel & LiveData with DataBinding

Integrate ViewModel with DataBinding

This avoids us the need to explicitly declare .setOnClickListener() manually and integrate it into the view.

Up until now, this was our MainActivity following MVVM architecture.

class MainActivity: AppCompatActivity() {

	private lateinit var viewModel: MainActivityViewModel
	
	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
		viewModel = ViewModelProviders.of(this).get(MainActivityViewMode::class)
		
		viewModel.count.observe(this, Observer {
			binding.countText.text = it.toString()
		})
		
		// we will remove methods such as this one
		binding.button.setOnClickListener {
			viewModel.updateCount()
		}
	}
}

We’re going to connect the ViewModel object with the DataBinding object and declare the method directly inside the .xml.

Open the layout activity_main.xml, for this case, and add <data> tags to assign the view model Object we created there. Be careful as the .xml layouts need to be surrounded by <layout> tags.

<layout>
	<data>
		<variable
		  name="myViewModel"
		  type="es.codes.mario.templates.MainActivityViewModel"/>
	</data>
</layout>

We go to the buttons declaration inside the same .xml file and change the onClick="" variable. This will bind the expression.
(!) To do this, our gradle version has to be higher than 2.0 (!)

<Button 
	...
	android:onClick="@{()->myViewModel.updateCount()}"/>

Now we go back to our class, add the code to bind it, and remove the code for the listener.

class MainActivity: AppCompatActivity() {
	private lateinit var viewModel: MainActivityViewModel
	
	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
		viewModel = ViewModelProviders.of(this).get(MainActivityViewMode::class)
		
		// add this here to bind it
		binding.myViewModel = viewModel
		
		viewModel.count.observe(this, Observer {
			binding.countText.text = it.toString()
		})
	
		// delete setOnClickListener method
	}
}

Integrate LiveData with DataBinding

This avoids us the need to explicitly declare the Observer.

class MainActivity: AppCompatActivity() {
	private lateinit var viewModel: MainActivityViewModel
	
	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
		viewModel = ViewModelProviders.of(this).get(MainActivityViewMode::class)
		binding.myViewModel = viewModel
		
		// now we will remove methods such as this one
		viewModel.count.observe(this, Observer {
			binding.countText.text = it.toString()
		})
	
	}
}

Instead we will use LiveData as a DataBinding source in the .xml layout file.

We want to display the following count variable, but it’s an Int. To display it, we need to convert that Int to String.

class MainActivityViewModel: ViewModel() {
	var count = MutableLiveData<Int>()
	...
}

We go to activity_main.xml and declare android:text tag. Then we will use LiveData directly as the DataBinding source.

<TextView
	...
	android:text="@{String.valueOf(myViewModel.count)}"
	... />

There’s one special thing left to do. LiveData is always associated with the lifecycle of an activity or a service, so we have to provide the actual lifecycle owner to the view model object. In MainActivity we need to set the current activity as the lifecycle owner of the binding object.

class MainActivity: AppCompatActivity() {
	private lateinit var viewModel: MainActivityViewModel
	
	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
		viewModel = ViewModelProviders.of(this).get(MainActivityViewMode::class)
		
		// add this
		binding.lifecycleOwner = this
		binding.myViewModel = viewModel
	}
}

encapsulate data

If we want to encapsulate data for more security, we may declare it as private and create a public variable to access it.

class MainActivityViewModel: ViewModel() {
	private var count = MutableLiveData<Int>()
	val countData: LiveData<Int>
	get() = count
	
	...
}

We must also change the .xml value.

<TextView
	...
	android:text="@{String.valueOf(myViewModel.countData)}"
	... />

Two way data binding

We use one-way DataBinding to show the user some data or get user input.

With two way data binding when the value of the object changes, the UI changes, and when the UI changes, the value of the object changes.

We have the following ViewModel.

class MainActivityViewModel: ViewModel() {
	val userName = MutableLiveData<String>()
	
	init {
		userName.value = "Frank"
	}
}

Open main_activity.xml and surround the existing contents by <layout> tags.
Write <data> tags to reference MainActivityViewModel.
Bind the value of userName MutableLiveData to the TextView.

<layout>
	<data>
		<variable
			name="viewModel"
			type="es.codes.mario.templates.MainActivityViewModel"/>
	</data>
	<android.constraintlayout.widget.ConstraintLayout>
		<TextView
			...
			android:text="@{viewModel.userName}"
			... />
	</android.constraintlayout.widget.ConstraintLayout>
</layout>

Go to MainActivity and

  1. Define a DataBinding object.
  2. Replace setContentView().
  3. Define a reference variable for the view model.
  4. Since we are using livedata with data binding, set this activity as the lifecycle owner.
class MainActivity: AppCompatActivity() {

	private lateinit var binding: ActivityMainBinding
	private lateinit var viewModel: MainActivityViewModel
	
	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
		viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
		binding.viewModel = viewModel
		binding.lifecycleOwner = this
	}
	
}

Up until here this is one way data binding. The view displays LiveData values.

For two way data binding, bind the data in an EditText the same way, but use the expression @={} instead of @{}.

<layout>
	<data>
		<variable
			name="viewModel"
			type="es.codes.mario.templates.MainActivityViewModel"/>
	</data>
	<android.constraintlayout.widget.ConstraintLayout>
		<TextView
			...
			android:text="@{viewModel.userName}"
			... />
		
		<EditText
			...
			android:text="@={viewModel.userName}"
			... />
	</android.constraintlayout.widget.ConstraintLayout>
</layout>

Android LiveData

We use LiveData to observe data inside ViewModel from an Activity or Fragment. If any change or update to this data happens, we can automatically update the Activity or Fragment.

LiveData only updates observers in an active lifecycle state. Its benefits are to automatically update the UI when app data changes.

LiveData vs MutableLiveData

Data inside LiveData is read-only. It cannot be updated as it has no public method to modify it. MutableLiveData can be updated.

Implementation

Add the following into your App’s gradle.build

dependencies {
	def lifecycle_version = "2.1.0"
	implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
}

We have the following ViewModel.

class MainActivityViewModel(startingTotal: Int): ViewModel {
	
	var total = MutableLiveData<Int>()
	
	init {
		total.value = startingTotal
	}
	
	fun setTotal(input: Int) { total.value = input }
	
}

How we use it from another Activity.

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)
	
	viewModel = ...
	viewModel.total.observe(this, Observer {
		// this will be executed when MutableLiveData changes. 
		binding.resultTextView.text = it.toString()
	})
	
	button.setOnClickListener { viewModel.setTotal(5) }
}

How to encapsulate LiveData

We want to encapsulate total, as leaving it public is no good design.

  1. We mark it as private.
  2. We need to declare a new public variable. As we’re not going to edit it, it should be LiveData. We can use Kotlin backing property to return LiveData from a getter function.
class MainActivityViewModel(startingTotal: Int): ViewModel {
	private var total = MutableLiveData<Int>()
	val totalData: LiveData<Int>
	get() = total
	
	init {
		total.value = startingTotal
	}
	
	fun setTotal(input: Int) { total.value = input }
	
}

Now we may change the onCreate() method.

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)
	
	viewModel = ...
	viewModel.totalData.observe(this, Observer {
		// this will be executed when MutableLiveData changes. 
		binding.resultTextView.text = it.toString()
	})
	
	button.setOnClickListener { viewModel.setTotal(5) }
}

Java Knowledge

This are my Java-related notes. Here I have all the knowledge I refer to when I have doubts about how to use or how to implement a framework / feature I’ve already implemented once.

Version changes

Interesting changes, new functionality and APIs that come to Java with each new version. They don’t include the full changes but the ones I deemed most useful or most interesting.

From Java 8 to Java 11
Java12
Java13

Experience

Small, functional snipets on how to implement a determined feature.

Java experience sheet
How to create a database intermediate table
Java date time API
New script files in Java

Frameworks

How to use and implement determined frameworks in a Java project (using Maven).

Spring in Action (Book)
Spring Cache
Spring Beans
Thymeleaf
Spring Cors

Maven (builder)
Testing (JUnit, TestNG, Mockito)
Vert.x (microservices)
Lombok (builder)
MapStruct (mapper)
Liquibase (database version control)

Android DataBinding

DataBinding

The improvement of DataBinding vs findViewById() is performance and cleanliness.

For larger apps where we have a lot of views, findViewById is way to slow as it has to search through all layouts every time. This doesn’t happen with DataBinding.

The usage of DataBinding eliminates the need for findViewById().

Implementation

Go to build.gradle file and enable it.

android {
	buildFeatures {
		dataBinding = true
	}
}

Read More

Android MVVM Architecture

Model View ViewModel is an android architecture model. It separates data-presentation logic from business logic. It’s designed to store and manage UI-related data.

  • Model - Application data logic. Plain Objects. API processing classes. Databases.
  • View - Screen’s layout. Houses all widgets for displaying information.
  • ViewModel - Business Logic. Object which describes the behaviour of view logic depending on the result of the model work.

We usually create one ViewModel for an activity or a fragment. Sometimes ViewModels can be shared by two or more Activities or Fragments. ViewModel are created when the activity is created, and they live until the activity is cleared from memory. Therefore ViewModel can only hold values which belong to the activity.

Implementation

We will have 3 packages: model, view, and viewmodel.

Inside model, we have data classes such as this one

data class Country(val countryName: String?)

Read More

Generics and Collections

Using Method References

Like lambdas, they make code easier to read.

We have a functional interface

@FunctionalInterface
public interface LearnToSpeak {
	void speak(String sound);
}

public class DuckHelper {
	public static void teacher(String name, LearnToSpeak trainer) {
		trainer.speak(name);
	}
}

public class Duckling {
	public static void makeSound(String sound) {
		LearnToSpeak learner = s -> System.out::println;
		DuckHelper.teacher(sound, learner);
	}
}

There’re four formats for methods references

Calling Static Methods

The Collection class has a staic method that can be used for sorting. The Consumer functional interface takes one param and does not return anthing.

Consumer<List<Integer>> methodRef = Collections::sort;
Consumer<List<Integer>> lambda = x -> Collections.sort(x);

Calling Instance Methods on a Particular Object

The String class has a startsWith() method that takes one param and returns a boolean.

var str = "abc";
Predicate<String> methodRef = str::startsWith;
Predicate<String> lambda = s -> str.startsWith(s);

A method reference doesn’t have to take any parameters. For the next example, we use a Supplier, which takes zero params and returns a value.

var random = new Random();
Supplier<Integer> methodRef = random::nextInt;
Supplier<Integer> lambda = () -> random.nextInt();

Calling Instance Methods on a Parameter

We call an instance method that doesn’t take any params. The trick is that we do so without knowing the instance in advance.

Predicate<String> methodRef = String::isEmpty;
Predicate<String> lambda = s -> s.isEmpty();

It looks like a static method, but it isn’t.

You can even combine the two types of instance method references with a BiPredicate, which takes two params and returns a boolean.

BiPredicate<String, String> methodRef = String::startsWith;
BiPredicate<String, String> lambda = (s, p) -> s.startsWith(p);

Calling Constructors

A constructor reference is a special type of method reference that uses new instead of a method, and it instantiates an object. It’s common for a constructor reference to use a Supplier as shown here.

Supplier<List<String>> methodRef = ArrayList::new;
Supplier<List<String>> lambda = () -> new ArrayList();

Number of Params in a Method Reference

A method reference can look the same, even when it will behave differently based on the surrounding context.

Given the following method

public class Penguin {
	public static Integer countBabies(Penguin... cuties) {
		return cuties.length;
	}
}

The method can be interpreted in three ways, for zero or more values.

// no param
Supplier<Integer> methodRef = Penguin::countBabies;
Supplier<Integer> lambda = () -> Penguin.countBabies();

// one param
Function<Penguin, Integer> methodRef = Penguin::countBabies;
Function<Penguin, Integer> lambda = (x) -> Penguin.countBabies(x);

// multiple params
BiFunction<Penguin, Penguin, Integer> methodRef = Penguin::countBabies;
BiFunction<Penguin, Penguin, Integer> lambda =
	(x, y) -> Penguin.countBabies(x, y);

Using Lists, Sets, Maps, and Queues

A collection is a group of objects contained in a single object, There are four main interfaces.

  • List - Ordered collection of elements that allows duplicate entries. It can be accesed by an int index.
  • Set - collection that does not allow duplicate entries.
  • Queue - Collection that orders its elements in an specific order for processing. A typical queue is FIFO or LIFO.
  • Map - Collection that maps keys to values, with no duplicate keys allowed. The elements in it are key/value pairs.

(add image of Collections hierarchy)

Common Collections Methods

add()

Inserts a new element into the Collection and returns whether it was successful.

boolean add(E element)

Usage

Collection<String> list = new ArrayList<>();
sout(list.add("one")); // true
sout(list.add("one")); // true

Collection<String> set = new HashSet<>();
sout(set.add("one")); // true
sout(set.add("one")); // false

remove()

Removes a single matching value and returns whether it was successful.

boolean remove(Object object)

Usage

Collection<String> birds = new ArrayList<>();
birds.add("hawk");
birds.add("hawk");
sout(birds.remove("cardinal"));  // false
sout(birds.remove("hawk")); // true
sout(birds); // hawk
Deleting while Looping

Java does not allow removing elements from a list while using the enhanced for loop.

Collection<String> birds = new ArrayList<>();
birds.add("hawk");
birds.add("hawk");
birds.add("hawk");

for(String bird : birds) {
	birds.remove(bird); // ConcurrentModificationException
}

isEmpty() and size()

They look at how many elements are in the Collection.

boolean isEmpty()
int size()

clear()

It provides an easy way to discard all elements of the Collection.

void clear()

How to use it

Collection<String> birds = new ArrayList<>();
birds.add("hawk"); [hawk]
birds.add("hawk"); [hawk, hawk]
birds.clear(); []

contains()

It checks whether a certain value is in the Collection.

boolean contains(Object object)

Usage

 Collection<String> birds = new ArrayList<>();
 birds.add("hawk");
 sout(birds.contains("hawk")); // true
 sout(birds.contains("robin")); // false

removeIf()

It removes all elements that match a condition.

boolean removeIf(Predicate<? super E> filter)

It uses a Predicate.

Collection<String> list = new ArrayList<>();
list.add("Magician"); [Magician]
list.add("Assistant"); [Magician, Assistant]
list.removeIf(s -> s.startsWith("M")); [Assistant]

forEach()

We use it to loop through a Collection.

void forEach(Consumer<? super T> action)

Usage

Collection<String> cats = Arrays.asList("Annie", "Ripley");
cats.forEach(System.out::println);

Using the List Interface

You use a list when you want an ordered collection that can contain duplicate entries.
Items can be retrieved and inserted at specific positions in the list, based on an int index. Unlike an array, many List implementations can change in size after they’re declared.

Comparing List Implementations

An ArrayList is like a resizable array. When elements are added, it automatically grows.
Its main benefit, is that you can look up any element in constant time. Adding or removing an element is slower than accessing an element. It’s a good choice when you’re reading more often than writing.

A LinkedList is special because it implements both List and Queue. It has all the methods of a List. It also has additional methods to facilitate adding or removing from the beginning and/or end of the list.
It’s mein benefits are that you can access, add, and remove from the beginning and end of the list. The trade-off is that dealing with an arbitrary index takes linear time. This makes it a good choice when you’ll be using it as a Queue.

Creating a List with a Factory

There’re a few methods that let you create a List back, but don’t know the type of it.

Method Description Can add elements? replace? delete?
Arrays.asList(varargs) Returns fixed size list backed by an array no yes no
List.of(varargs) Returns immutable list no no no
List.copyOf(collection) Returns immutable list with copy of original collection’s values no no no

Immutable lists throw an UnsupportedOperationException when adding or removing a value.

Working with List Methods

This methods are for working with indexes.

Method Description
boolean add(E element) Adds element to end
void add(int index, E element) Adds element at index
E get(int index) Returns element at index
E remove(int index) Removes element at index
void replaceAll(UnaryOperator<E> op) Replaces each element in the list with the result of the operator
E set(int index, E e) Replaces element at index and returns original. Throws IndexOutOfBoundsException if the index is larger than maximum.
List<String> list = new ArrayList<>();
list.add("SD"); // [SD]
list.add(0, "NY"); // [NY, SD]
list.set(1, "FL"); // [NY, FL]
sout(list.get(0)); // NY
list.remove("NY"); // [FL]
list.remove(0); // []
list.set(0, "?"); // IndexOutOfBoundsException

Now, let’s look at using the replaceAll() method. It takes a UnaryOperator that takes one parameter and returns a value of the same type.

List<Integer> numbers = Arrays.asList(1, 2, 3);
numbers.replaceAll(x -> x*2); // [2, 4, 6]

Using the Set Interface

You use a set when you don’t want to allow duplicate entries and you aren’t concerned with the order.

Comparing Set Implementations

A HashSet stores its elements in a hash table, which means the keys are a hash and the values are an Object. This means that it uses the hashCode() method of the objects to retrieve them more efficiently.
The main benefit is that adding elements and checking whether an element is in the set both have constant time. The trade-off is that you lose the order in which you inserted the elements.

A TreeSet stores its elements in a sorted tree structure. The main benefit is that the set is always in sorted order. The trade-off is that adding and checking whether an element exists take longer than with a HashSet.

Working with Set Methods

Like List, you can create an immutable Set in one line or make a copy of an existing one.

Set<Character> letters = Set.of('z', 'o', 'o');
Set<Character> copy = Set.copyOf(letters);
Set<Integer> set = new HashSet<>();
boolean b1 = set.add(66); // true
boolean b2 = set.add(10); // true
boolean b3 = set.add(66); // false
boolean b4 = set.add(8); // true

Using the Queue Interface

You use a queue when elements are added and removed in a specific order. They’re typically used for sorting elements prior to processing them.

Comparing Queue Implementations

We use LinkedList, as in addition to being a list, it’s a double-ended queue. It’s different from a regular queue in that you can insert and remove elements from both the front and back of the queue. It’s main benefit is that it implements both List and Queue interfaces. The trade-off is that it isn’t as efficient as a pure queue.

Working with Queue Methods

|Method|Description|Throws exception on failure| |:—:|:—:|:—:| |boolean add(E e)|Adds an element to the back of the queue and returns true or throws exception|Yes| |E element()|Returns next element or throws exception if empty|yes| |boolean offer(E e)|Adds an element to the back of the queue and returns if it was successful|No| |E remove()|Removes and returns next element or throws exception if empty|yes| |E poll()|removes and returns next element or returns null if empty|no| |E peek()|returns next element or returns null is empty|no|

Queue<Integer> queue = new LinkedList<>();
queue.offer(10); // true
queue.offer(4); // true
queue.peek(); // 10
queue.poll(); // 10
queue.poll(); // 4
queue.peek(); // null

Using the Map Interface

You use a map when you want to identify values by a key. All Map classes have keys and values.

Comparing Map Implementations

A HahsMap stores the keys in a hash table. This means that it uses the hashCode() method of the keys to retrieve their values more effitiently.
The main benefit is that adding elements and retrieving the element by key both have constant time. The trade-off is that you lose the order in which you inserted the elements.

A TreeMap stores the keys in a sorted tree structure. The main benefit is that keys are always in a sorted order. The trade-off is that adding and checking whether a key is present takes longer as the tree grows larger.

Working with Map Methods

|Method|Description| |:—:|:—:| |void clear()|Removes all keys and values| |boolean containsKey(Object key)|Returns whether key is in map| |boolean containsValue(Object value)|Returns whether value is in map| |Set<Map.Entry<K,V>> entrySet()|Returns a Set of key/value pairs| |void forEach(BiConsumer(K key, V value))|Loop through each key/value pair| |V get(Object key)|Returns the value mapped by key or null| |V getOrDefault(Object key, V defaultValue)|Returns the value mapped by the key or the default value| |boolean isEmpty()|Returns whether the map is empty| |Set<K> keySet()|Returns set of all keys| |V merge(K key, V value, Function(<V, V, V> func))|Sets value if key is not set. Runs the function if the key is set to determine the new value. Removes if null| |V put(K key, V value)|Adds or replaces key/value pair. Returns previous value or null| |V putIfAbsent(K key, V value)|Adds value if key not present and returns null. Otherwise, returns existing value.| |V remove(Object key)|Removes and returns value mapped to key or null| |V replace(K key, V value)|Replaces the value for a given key if the key is set or null| |void replaceAll(BiFunction<K,V, V> func)|Replaces each value with the results of the function| |Collection<V> values()|Returns Collection of all values|

forEach() and entrySet()

The map version has two parameters - key and value.

Map<Integer, Character> map = new HashMap<>();
map.put(1, 'a');
map.put(2, 'b');
map.put(3, 'c');
map.forEach((k, v) -> sout(v))
map.values().forEach(System.out::println);
map.entrySet().forEach(e -> sout(e.getKey() + e.getValue());
replace() and replaceAll()
Map<Integer, Integer> map = new HashMap<>();
map.put(1, 2);
map.put(2, 4);
Integer original = map.replace(2, 10); // 4
map.replaceAll((k, v) -> k + v); // {1=3, 2=12}
putIfAbsent()

This sets a value but skips it if the value is already set to a non-null value.

merge()

It adds logic of what to choose.

The next example takes two String as input, and a BiFunction which is the logic. In this case it takes the longest String.

BiFunction<String, String, String> mapper = (v1, v2) -> v1.length() > v2.length ? v1 : v2;

Map<String, String> favorites = new HashMap<>();
favorites.put("Jenny", "Bus Tour");
favorites.put("Tom", "Tram");

String jenny = favorites.merge("Jenny", "Skyride", mapper);
String tom = favorites.merge("Tom", "Skyride", mapper);
// {Tom = Skyride, Jenny = Bus Tour}

(for more information on merge() check page 130 of 2nd book)

Sorting Data

(!) Comparable and Comparator are similar enough to be tricky. The exam likes to see if it can trick you into mixing the two. (!)

Creating a Comparable Class

Comparable has just one Method.

public interface Comparable<T> {
	int compareTo(T o);
}

As it implements T, any object can be Comparable.

public class Duck implements Comparable<Duck> {
	private String name;

	public Duck(String name) {
		this.name = name;
	}

	public int compareTo(Duck d) {
		return name.compareTo(d.name); // sorts ascendingly by name
	}

	public static void main(String[] args) {
		var ducks = new ArrayList<Duck>();
		duck.add(new Duck("Quack"));
		duck.add(new Duck("Puddles"));
		Collections.sort(duck); // sort by name
	}

}

Here, Duck implements compareTo(). Since Duck is comparing objects of type String, and it already has a compareTo() method, it can just delegate.

There are three rules to know about the compareTo()

  • 0 is returned, when the current object is equivalent to the argument to compareTo()
  • A negative number, when the current object is smaller than the argument to compareTo()
  • A positive number, when the current object is larger than the argument to compareTo()
public class Animal implements Comparable<Animal> {
	private int id;

	public int compareTo(Animal a) {
		return id - a.id; // sorts ascending by id
	}

	public static void main(String[] args) {
		var a1 = new Animal();
		var a2 = new Animal();
		a1.id = 5;
		a2.id = 7;
		sout(a1.compareTo(a2)); // -2
		sout(a1.compareTo(a1)); // 0
		sout(a2.compareTo(a1)); // 2
	}
}

Casting the compareTo() Argument

When dealing with legacy code or code that does not use generics, the compareTo() method requires a cast since it’s passed an Object.

public class LegacyDuck implements Comparable {
	private String name;

	public int compareTo(Object obj) {
		LegacyDuck d = (LegacyDuck) obj; // cast because no generics
		return name.compareTo(d.name);
	}
}

Checking for null

When writing your own compare methods, you should check the data before comparing it, if it’s not validated ahead of time.

public class MissingDuck implements Comparable<MissingDuck> {
	private String name;

	public int compareTo(MissingDuck quack) {
		if(quack == null) {
			throw new IllegalArgumentException("Poorly formed duck!");
		}

		if(this.name == null && quack.name == null) {
			return 0;
		} else if(this.name == null) {
			return -1;
		} else if(quack.name == null) {
			return 1;
		} else {
			return name.compareTo(quack.name);
		}
	}
}

Keeping compareTo() and equals() Consistent

The compareTo() method returns 0 if two objects are equal, while equals() returns true if two objects are equal.

Comparing Data with a Comparator

Sometimes you want to sort an object that did not implement Comparable, or you want to sort objects in different ways at different times.

Comparator is a functional interface.

public class Duck implements Comparable<Duck> {

private String name;
	private int weight;

	// constructor
	// getters & setters...

	public String toString() {
		return name;
	}

	public int compareTo(Duck d) {
		return name.compareTo(d.name);
	}

	public static void main(String[] args) {
		var ducks = new ArrayList<Duck>();
		ducks.add(new Duck("Quack", 8));
		ducks.add(new Duck("Puddles", 10));

		// Comparator<Duck> byWeight = (d1, d2) -> d1.getWeight() - d2.getWeight();
		Comparator<Duck> byWeight = Comparator.comparing(Duck::getWeight);
		Collections.sort(ducks); // [Puddles, Quack]
		Collections.sort(ducks, byWeight); // [Quack, Puddles]
	}

}

(!) Comparable can be used without an import statement. Comparator cannot (!)

Comparing Comparable and Comparator

|Difference|Comparable|Comparator| |:—:|:—:|:—:| |Package name|java.lang|java.util| |Interface must be implemented by class comparing?|Yes|No| |Method name in interface|compareTo()|compare()| |Number of parameters|1|2| |Common to declare using a lambda|No|Yes|

(!) The exam will try to trick you by mixing up the two and seeing if you can catch it. It also may try to trick you with the methods’ names. Pay attention when you see Comparator and Comparable in questions (!)

var byWeight = new Comparator<Duck>() { // DOES NOT COMPILE
	public int compareTo(Duck d1, Duck d2) {
		return d1.getWeight() - d2.getWeight();
	}
}

The previous code doesn’t compile. The method name is wrong. A Comparator must implement a method named compare().

Comparing Multiple Fields

When comparing multiple instance variables, the code gets messy.

public class Squirrel {
	private int weight;
	private String species;

	// constructors
	// getters / setters
}

We want to sort first by species name, then if they are the same species, by weight.

public class MultiFieldComparator implements Comparator<Squirrel> {
	public int compare(Squirrel s1, Squirrel s2) {
		int result = s1.getSpecies().compareTo(s2.getSpecies());
		if(result != 0) {
			return result;
		}

		return s1.getWeight() - s2.getWeight();
	}
}

This works assuming no null values.

Alternatively, we can use method references and build the comparator. This does the same

Comparator<Squirrel> c = Comparator.comparing(Squirrel::getSpecies)
	.thenComparingInt(Squirrel::getWeight);

Suppose we want to sort in descending order by species.

var c = Comparator.comparing(Squirrel::getSpecies).reversed();
Helper static methods for Comparator

|Method|Description| |:—:|:—:| |comparing(function)|Compare by the results of a function that returns any Object| |comparingDouble(function)|Compare by the results of a function that returns a double| |comparingInt(function)|Compare by the results of a function that returns an int| |comparingLong(function)|Compare by the results of a function that returns a long| |naturalOrder()|Sort using the order specified by the Comparable implementation on the object itself| |reverseOrder()|Sort using the reverse of the order specified by the Comparable interface on the object itself|

Helper default methods for building a Comparator

|Method|Description| |:—:|:—:| |reversed()|Reverse the order of the chained Comparator| |thenComparing(function)|If the previous Comparator returns 0, use this comparator that returns an Object| |thenComparingDouble(function)|If the previous Comparator returns 0, use this comparator that returns a double| |thenComparingInt(function)|If the previous Comparator returns 0, use this comparator that returns an int| |thenComparingLong(function)|If the previous Comparator returns 0, use this comparator that returns a long|

Sorting and Searching

Now that we’ve learned about Comparable and Comparator, we can finally do something useful, like sorting. The Collections.sort() method uses the compareTo() method to sort. It expects the objects to be sorted to be Comparable.

public class SortRabbits {
	static class Rabbit { int id; }

	public static void main(String[] args) {
		List<Rabbit> rabbits = new ArrayList<>();
		rabbits.add(new Rabbit());
		Collections.sort(rabbits); // DOES NOT COMPILE
	}
}

(!) This doesn’t compile, because Rabbit is not Comparable. You can fix this by passing a Comparator to sort()(!)

public class SortRabbits {
	static class Rabbit { int id; }

	public static void main(String[] args) {
		List<Rabbit> rabbits = new ArrayList<>();
		rabbits.add(new Rabbit());
		Comparator<Rabbit> c = (r1, r2) -> r1.id -> r2.id;
		Collections.sort(rabbits, c);
	}
}

The sort() and binarySearc() methods allow you to pass in a Comparator object, when you don’t want to use natural order.

Reviweing binarySearch()

The binarySearch() requires a sorted List.

List<Integer> list = Arrays.asList(6, 9, 1, 8);
Collections.sort(list); // [1, 6, 8, 9]
Collections.binarySearch(list, 6); // 1
Collections.binarySearch(list, 3); // -2

(I left info out, more on this at page 139)

Working with Generics

Generic Classes

The syntax for introducing a generic is to declare a formal type parameter in angle brackets.

public class Crate<T> {
	private T contents;

	public T emptyCrate() {
		return contents;
	}

	public void packCrate(T contents) {
		this.contents = contents;
	}
}

This are the naming conventions for generics.

Name Usage
E Element
K Map key
V Map Value
N number
T generic data type
S, U, V… multiple generic types
Elephant elephant = new Elephant();
Crate<Elephant> crateForElephant = new Crate<>();
crateForElephant.packCrate(elephant);
Elephant inNewHome = crateForElephant.emptyCrate();

Generic classes aren’t limited to having a single type parameter.

public class SizeLimitedCrate<T, U> {
	private T contents;
	private U sizeLimit;

	// constructor
}
Elephant elephant = new Elephant();
Integer numPounds = 15_000;
SizeLimitedCrate<Elephant, Integer> c1 = new SizeLimitedCrate<>(elephant, numPounds);

Generic Interfaces

Just like a class, an interface can declare a formal type parameter.

public interface Shippable<T> {
	void ship(T t);
}
class ShippableRobotCrate implements Shippable<Robot> {
	public void ship(Robot t) {}
}
class ShippableAbstractCrate<U> implements Shippable<U> {
	public void ship(U t);
}

Raw Types

The final way is to not use generics at all. This is the old way of writing code. It generates a compiler warning, but it does compile.

class ShippableCrate implements Shippable {
	public void ship(Object t) {}
}

Generic Methods

This is often useful for static methods since they aren’t part if an instance that can declare the type.

public class Handler {
	public static <T> void prepare(T t) {}

	public static <T> Crate<T> ship(T t) {}
}

(!) Unless a method is obtaining the type from the class/interface, it’s specified immediately before the return type of the method. (!)

public class More {
	public static <T> void sink(T t) {}
	public tatic <T> T identity(T t) { return t; }
	public static T noGood(T t) { return t; } // DOES NOT COMPILE
}

When you have a method declare a generic param type, it’s independent of the class generics.

public class Crate<T> {
	public <T> T tricky(T t) {
		return t;
	}
}
public static String createName() {
	Crate<Robot> crate = new Crate<>();
	return crate.tricky("bot");
}

Bounding Generic Types

(more on this on page 145~ from 2nd book)

They restrict what types can be used in a wildcard. A wildcard generic type is an unknown generic type represented with a question mark ?.

Type of bound Syntax Example
Unbounded ? List<?> a = new ArrayList<String>();
Upper bound ? extends type List<? extends Exception> a = new ArrayList<RuntimeException>();
Lower bound ? super type List<? super Exception> a = new ArrayList<Object>();

Unbounded Wildcards

An unbounded wildcard represents any data type. You use ? when you want to specify that any type is okay.

public static void printList(List<?> list) {
	for(Object x : list)
		sout(x);
}

public static void main(String[] args) {
	List<String> keywords = new ArrayList<>();
	keywords.add("java");
	printList(keywords);
}

These two statements are not equivalent

List<?> x1 = new ArrayList<>();
var x2 = new ArrayList<>();

The first is of type List, while the second is of type ArrayList. Also, we can only assign x2 to a List<Object>.

Upper-Bounded Wildcards

(!) A generic type can’t juse use a subclass, instead they use wildcards. (!)

ArrayList<Number> list = new ArrayList<Integer>(); // DOES NOT COMPILE
// instead, use
List<? extends Number> list = new ArrayList<Integer>();

The upper-bounded wildcard says that any class that extends Number or Number itself can be used as a formal param type.

public static long total(List<? extends Number> list) {
	long count = 0;
	for(Number number : list) {
		count += number.longValue();
	}
	return count;
}

When we work with upper bounds or unbounded wildcards, the list becomes logically immutable and therefore cannot be modified.

static class Sparrow extends Bird {}
static class Bird {}

public static void main(String[] args) {
	List<? extends Bird> birds = new ArrayList<Bird>();
7:	birds.add(new Sparrow()); // DOES NOT COMPILE
8:	birds.add(new Bird()); // DOES NOT COMPILE
}

The problem stems from the fact that Java doesn’t know what type List<? extends Bird> really is. It could be List<Bird> or List<Sparrow>.
Line 7 doesn’t compile because we can’t add a Sparrow to List<? extends Bird>.
Line 8 doesn’t compile because we can’t add a Bird to List<Sparrow>.

(!) Upper bounds are like anonymous classes in that they use extends regardless of whether we’re working with a class or an interface. (!)

Lower-Bounded Wildcards

I want to give a method a param which may be a List<String> or List<Object>.

We have the following “solutions”

  Method compiles Can pass List<String> Can pass List<Object>
List<?> No (unbounded generics are immutable) Yes Yes
List<? extends Object> No (upper-bounded generics are immutable) Yes Yes
List<Object> Yes No (with generics, must pass exact match) Yes

To solve this problem, we need to use a lower bound.

public static void addSound(List<? super String> list) {
	list.add("quack");
}

With a lower bound, we are telling Java that the list will be a list of String objects or a list of some objects that are superclass of String.

(check again page 148 from 2nd book. there’s more than this)

Summary

A method reference is a compact syntax for writing lambdas that refer to methods. There are four types:

  • static methods
  • instance methods on a particular object
  • instance methods on a parameter
  • constructor references.

Each primitive class has a corresponding wrapper class. For example, long’s wrapper class is Long. Java can automatically convert between primitive and wrapper classes when needed. This is called autoboxing and unboxing. Java will use autoboxing only if it doesn’t find a matching method signature with the primitive. For example, remove(int n) will be called rather than remove(Object o) when called with an int.

The diamond operator <> is used to tell Java that the generic type matches the declaration without specifying it again. The diamond operator can be used for local variables or instance variables as well as one-line declarations.

The Java Collections Framework includes four main types of data structures: lists, sets, queues, and maps.
The Collection interface is the parent interface of List, Set, and Queue. The Map interface does not extend Collection. You need to recognize the following:

  • List: An ordered collection of elements that allows duplicate entries
    • ArrayList: Standard resizable list
    • LinkedList: Can easily add/remove from beginning or end
  • Set: Does not allow duplicates
    • HashSet: Uses hashCode() to find unordered elements
    • TreeSet: Sorted. Does not allow null values
  • Queue: Orders elements for processing
    • LinkedList: Can easily add/remove from beginning or end
  • Map: Maps unique keys to values
    • HashMap: Uses hashCode() to find keys
    • TreeMap: Sorted map. Does not allow null keys

The Comparable interface declares the compareTo() method. This method returns a negative number if the object is smaller than its argument, 0 if the two objects are equal, and a positive number otherwise. The compareTo() method is declared on the object that is being compared, and it takes one parameter. The Comparator interface defines the compare() method. A negative number is returned if the first argument is smaller, zero if they are equal, and a positive number otherwise. The compare() method can be declared in any code, and it takes two parameters. Comparator is often implemented using a lambda.

The Arrays and Collections classes have methods for sort() and binarySearch(). Both take an optional Comparator parameter. It is necessary to use the same sort order for both sorting and searching, so the result is not undefined.

Generics are type parameters for code. To create a class with a generic parameter, add <T> after the class name. You can use any name you want for the type parameter. Single uppercase letters are common choices.

Generics allow you to specify wildcards. <?> is an unbounded wildcard that means any type. <? extends Object> is an upper bound that means any type that is Object or extends it. <? extends MyInterface> means any type that implements MyInterface. <? super Number> is a lower bound that means any type that is Number or a superclass. A compiler error results from code that attempts to add an item in a list with an unbounded or upper-bounded wildcard.

Exam Essentials

Translate method references to the “long form” lambda. Be able to convert method references into regular lambda expressions and vice versa. For example, System.out::print and x -> System.out.print(x) are equivalent. Remember that the order of method parameters is inferred for both based on usage.

Use autoboxing and unboxing. Autoboxing converts a primitive into an Object. For example, int is autoboxed into Integer. Unboxing converts an Object into a primitive. For example, Character is autoboxed into char.

Pick the correct type of collection from a description. A List allows duplicates and orders the elements. A Set does not allow duplicates. A Queue orders its elements to facilitate retrievals. A Map maps keys to values. Be familiar with the differences of implementations of these interfaces.

Work with convenience methods. The Collections Framework contains many methods such as contains(), forEach(), and removeIf() that you need to know for the exam. There are too many to list in this paragraph for review, so please do review the tables in this chapter.

Differentiate between Comparable and Comparator. Classes that implement Comparable are said to have a natural ordering and implement the compareTo() method. A class is allowed to have only one natural ordering. A Comparator takes two objects in the compare() method. Different Comparators can have different sort orders. A Comparator is often implemented using a lambda such as (a, b) -> a.num – b.num.

Write code using the diamond operator. The diamond operator (<>) is used to write more concise code. The type of the generic parameter is inferred from the surrounding code. For example, in List<String> c = new ArrayList<>(), the type of the diamond operator is inferred to be String.

Identify valid and invalid uses of generics and wildcards. <T> represents a type parameter. Any name can be used, but a single uppercase letter is the convention. <?> is an unbounded wildcard. <? extends X> is an upper-bounded wildcard and applies to both classes and interfaces. <? super X> is a lower-bounded wildcard.

Oracle 1Z0-819 (Java11) Certification - Index

The new 1Z0-819 certification is the combination of the old existing ones (1Z0-815 & 1Z0-816) together.

OCP Java SE 11 Programmer I - Study guide for 1Z0-815

Welcome to Java
Java Building Blocks
Java Operators
Making Decisions
Core Java APIs
Lambdas and Functional Interfaces
Methods and Encapsulation
Class Design
Advanced Class Design
Exceptions
Java Modules

OCP Java SE 11 Programmer II - Study guide for 1Z0-816

Java Fundamentals
Java Annotations
Generics and Collections

Java Annotations

Introducing Annotations

Annotations are all about metadata.

Purpose of Annotations

The purpose of an annotation is to assign metadata attributes to classes, methods, variables and other Java types.

They function a lot like interfaces. They allow us to mark a class, without chaning its inheritance structure. While interfaces can be applies only to classes, annotations can be applied to classes, methods, expressions and other annotations. Unlike interfaces, annotations allow us to pass a set of values where they’re applied.

public class Veterinarian {
	@ZooAnimal(habitat="Infirmary")
	private Lion sickLion;

	@ZooAnimal(habitat="Safari")
	private Lion healthyLion;

	@ZooAnimal(habitat="Special Enclosure")
	private Lion blindLion;
}

This class defines three variables, each with an associated habitat value. The habitat is part of the type declaration of each variable, not an individual object.

public class Lion {
	@ZooSchedule(hours={"9am", "5pm", "10pm"})
	void feedLions() {
		sout("time to feed the lions!");
	}
}

public class Peacock {
	@ZooSchedule(hours={"4pm", "5pm"})
	void cleanPeacocksPen() {
		sout("time to sweep up!");
	}
}

With this approach, the task and its schedule are defined right next to each other.

Rules of annotations

  1. Annotations function a lot like interfaces. (They allow us to mark a class without changing its inheritance structure)
  2. Annotations establish relationships that make it easier to manage data about our application.
  3. An annotation ascribes custom information on the declaration where it’s defined. (The same annotation can often be applied to completely unrelated classes or variables)
  4. Annotations are optional metadata and by themselves do not do anything. (You have to be able to take a project filled with thousands of annotations, remove all of them, and it will still compile and run. Just with potentially different behaviour at runtime)
  5. To use an annotation, all required values must be provided
  6. To declare a @Repeteable annotation, you must define a containing annotation type value

Creating Custom Annotations

Creating an Annotation

We use the @interface annotation to declare one. They’re commonly defined in their own file as a top-level type, although they can be defined inside a class declaration like an inner class.

public @interface Exercise {}

When declaring an annotation, any element without a default value, is considered required.

We use this interface like this

@Exercise() public class Cheetah {}

@Exercise public class Sloth {}

@Exercise public class ZooEmployee {}

Like interfaces, annotations can be applied to unrelated classes.
When using an annotation, parentheses are optional. Once we start adding elements, though, they’re required if the annotation includes any values.

If an annotation is declared on a line by itself, then it applies to the next nonannotation type found on the proceeding lines. This applies when there’re multiple annotations.

@Scaley		@Flexible
@Food("insect") public class Snake {}

Specifying a Required Element

An annotation element is an attribute that stores values about the particular usage of an annotation.

public @interface Exercise {
	int hoursPerDay();
}

Let’s check the usage

@Exercise(hoursPerDay=3) public class Cheetah {}
@Exercise hoursPerDay=3 public class Sloth {} // DOES NOT COMPILE
@Exercise public class ZooEmployee {} // DOES NOT COMPILE

The Sloth class does not compile because it’s missing parentheses.
The last one does not compile neither because hoursPerDay is required.

Providing an Optional Element

For an element to be optional, rather than required, it must include a default value.

public @interface Exercise {
	int hoursPerDay();
	int startHour() default 6;
}

Let’s apply it.

@Exercise(startHour=5, hoursPerDay=3) public class Cheetah {}
@Exercise(hoursPerDay=0) public class Sloth {}

@Exercise(hoursPerDay=7, startHour="8") // DOES NOT COMPILE
public class ZooEmployee {}

The order for each element does not matter.

The last one does not compile because the value type is invalid.

The default value of an annotation cannot be just any value. It has to be a non-null constant expression.

public @interface BadAnnotation {
	String name() default new String(""); // DOES NOT COMPILE
	String address() default "";
	String title() default null; // DOES NOT COMPILE
}

Selecting an Element type

An annotation element cannot be declared with just any type.
It has to be a

  • primitive
  • String
  • Class
  • enum
  • another annotation
  • an array of these types
public class Bear {}

public enum Size { SMALL, MEDIUM, LARGE }

public @interface Panda {
	Integer height(); // DOES NOT COMPILE
	String[][] generalInfo(); // DOES NOT COMPILE
	Size size() default Size.SMALL;
	Bear friendlyBear(); // DOES NOT COMPILE
	Exercise exervise() default @Exercise(hoursPerDay=2);
}

Wrapper classes like Integer or Long are not supported.
String[] is supported but String[][] is not.
Bear is not supported as is not Class.

Applying Element Modifiers

Annotation elements are implicitly abstract and public.

public @interface Material {}

public @interface Fluffy {
	int cuteness();
	public abstract int softness() default 11;

	protected Material material(); // DOES NOT COMPILE
	private String friendly(); // DOES NOT COMPILE
	final boolean isBunny(); // DOES NOT COMPILE
}

The elements material() and friendly() do not compile because the access modifier conflicts with the elements being implicitly public.
The method isBunny() does not compile because it cannot be marked final.

Adding a Constant Variable

Annotations can include constant variables that cna be accessed by other classes without actually creating the annotation.

public @interface ElectricitySource {
	public int voltage();
	int MIN_VOLTAGE = 2;
	public static final int MAX_VOLTAGE = 18;
}

They’re also implicitly public, static and final.

Reviewing Annotation Rules

public @interface Hazard {
	int danger();
	public String description() default "Fire";
	public static final int UNKNOWN = -1;
}

@Hazard(danger=100, description="Wind Damage")
public class Tornado {}

public @interface Hazard - public is optional, as it may be package-private.
public static final int UNKNOWN - public static final is implicitly applied, so they’re also optional.

Applying Annotations

Using Annotations in Declarations

They can be applied to a miriad of things. The following compiles, assuming they exist.

@FunctionalInterface interface Speedster {
	void go(String name);
}

@LongEars
@Soft
@Cuddly
public class Rabbit {

	@Deprecated
	public Rabbit(@NotNull Integer size) {}

	@Speed(velocity="fast")
	public void eat(@Edible String input) {
		@Food(vegetarian=true)
		String m = (@Tasty String) "carrots";

		Speedster s1 = new @Racer Speedster() {
			public void go(@FirstName @NotEmpty String name) {
				sout("start!" + name);
			}
		}

		Speedster s2 = (@Valid String n) -> sout(n);
	}

}

Mixing Required and Optional Elements

To use an annotation, all required values must be provided. While it may have many elements, valure are required only for ones without default values.

public @interface Swimmer {
	int armLength = 10;
	String stroke();
	String name();
	String favoriteStroke() default "Backstroke";
}

Implementations

@Swimmer class Amphibian {} // DOES NOT COMPILE
@Swimmer(favoriteStroke="Breaststroke", name="Sally") class Tadpole {} // DOES NOT COMPILE
@Swimmer(stroke="FrogKick", name="Kermit") clas Frog {}
@Swimmer(stroke="Butterfly", name="Kip", armLength=1) class Reptile {} // DOES NOT COMPILE
@Swimmer(strike="", name="", favoriteStroke="") class Snake {}

The 1st line does not compile because it’s missing the required elements stroke() and name().
The 2nd one does not compile neither as it’s missing the required element stroke().
The 4th lines does not compile as armLenght is a constant, not an element, and cannot be included in an annotation.

Creating a value() element

@Injured("Broken Tail") public class Monkey {}

Notice the previous annotation does only include a value and not a property name. This may compile as long as:

  • The annotation declaration has an element named value(), which may be optional or required.
  • The annotation declaration must not contain any other elements that are required.
  • The annotation usage must not provide values for any other elements.
public @interface Injured {
	String veterinarian() default "unassigned";
	String value() default "foot";
	int age() default 1;
}

Valid usages

public abstract class Elephant {
	@Injured("Legs") public void fallDown() {}
	@Injured(value="Legs") public abstract int trip();
	@Injured String injuries[];
}

The last one is allowed as @Injured does not have any required elements.
Typically, the value() of an annotation should be related ot its name.

(!) For the exam, make sure that if the shorthand notation is used, then there’s an element named value(). Also check that there’re no other required elements.
For example, the following declarations cannot be used with a shorthand annotation.

public @interface Sleep {
	int value();
	String hours();
}

public @interface Wake {
	String hours();
}
@Injured("Fur", age=2) public class Bear{} // DOES NOT COMPILE

The last annotation is not valid as it provides more than one value.

Passing an Array of Values

Annotations support a shorthand notation for providing an array that contains a single element.

public @interface Music {
	String[] genres();
}

If we want to provide only one value to the array, either way is correct

public class Giraffe {
	@Music(genres={"Rock and roll"}) String mostDisliked;
	@Music(genres="Classical") String favorite;
}

The first annotation is considered the regular form, as it’s clear the usage for an array.
The second one is the shorthand notation, where the array brackets are dropped for convenience. This notation can be used only if the array is composed of a single element.

public class Reindeer {
	@Music(genres="Blues", "Jazz") String favorite; // DOES NOT COMPILE
	@Music(genres=) mostDisliked; // DOES NOT COMPILE
	@Music(genres=null) String other; // DOES NOT COMPILE
	@Music(genres={}) String alternate;
}

While this shorthand notation can be used for arrays, it does not work for List or Collection.

Declaring Annotation-Specific Annotations

Limiting Usage with @Target

Many annotations declarations include @Target annotation, which limits the types the annotation can be applied to.
This takes an array of ElementType as its value() element.

ElementType Values

|ElementType value|Applies to| |:—:|:—:| |TYPE|Classes, interfaces, enums, annotations| |FIELD|Instance and static variables, enum values| |METHOD|method declarations| |PARAMETER|Constructor, method, lambda parameters| |CONSTRUCTOR|Constructor declarations| |LOCAL_VARIABLE|Local variables| |ANNOTATION_TYPE|Annotations| |PACKAGE|Packages declared in package-info.java| |TYPE_PARAMETER|Parameterized types, generic declarations| |TYPE_USE|Able to be applied anywhere there’s a Java type declared or used| |MODULE|Modules|

Some of this elements overlap.

(!) Make sure you’re able to properly recognize usage of them for the exam. (!)

@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface ZooAttraction {}

Understanding the TYPE_USE value

The TYPE_USE parameter can be used anywhere there’s a Java type.
By including it in @Target it actually includes nearly all the values, with a few exceptions, such as methods that return void.

It also allows annotations in places where types are used, such as cast operations, object creation with new, inside type declarations etc.

(!) For the exam, you only need to recognize that it can be applied in this manner if TYPE_USE is one of the @Target options (!)

@Target(ElementType.TYPE_USE)
@interface Technical {}

public class NetworkRepair {

	class OutSrc extends @Technical NetworkRepair {}

	public void repair() {
		var repairSubclass = new @Technical NetworkRepair() {};
		var o = new @Technical NetworkRepair().new @Technical OutSrc();
		int remaining = (@Technical int) 10.0;
	}

}

Storing Annotations with @Retention

The compiler discards certain types of information when converting your source code into a .class file. This may include annotations, which may be discarded by the compiler or at runtime.

This is specified by the @Retention annotation. It uses the following values of RetentionPolicy.

RetentionPolicy value Description
SOURCE Used only in the source file, discarded by the compiler
CLASS Stored in the .class file but not available at runtime (default compiler behaviour)
RUNTIME Stored in the .class file and available at runtime
@Retention(RetentionPolicy.CLASS) @interface Flier {}
@Retention(RetentionPolicy.RUNTIME) @interface Swimmer {}

Generating Javadoc with @Documented

If present, then the generated Javadoc will include annotation information defined on Java types.
Because it’s a marker annotation, it doesn’t take any values.

@Documented public @interface Hunter {}

@Hunter public class Lion {}

Here, the @Hunter annotation would be published with the Lion Javadoc information.

Inheriting Annotations with @Inherited

When this annotation is applied to a class, subclasses will inherit the annotation information found in the parent class.

@Inherited public @interface Vertebrate {}

@Vertebrate public class Mammal {}

public class Dolphin extends Mammal {} // includes @Vertebrate

Supporting Duplicates with @Repeatable

It’s used when you want to specify an annotation more than once on a type. Generally this happens when you want to apply the same annotation with different values.

public class Zoo {
	public static class Monkey {}

	@Risk(danger="Silly")
	@Risk(danger="Aggressive", level=5)
	@Risk(danger="Violent", level=10)
	private Monkey monkey;
}

(!) In the exam, if an annotation is being repeated for the same type, be sure it correctly implements @Repeteable (!)

To declare a @Repeteable annotation, you must define a containing annotation type value. A containing annotation type is a separate annotation that defines a value() array element. The type of this array is the particular annotation you want to repeat. By convention, the name is often the plural form of the repeteable annotation.

public @interface Risks {
	Risk[] value();
}

@Repeteable(Risks.class)
public @interface Risk {
	String danger();
	int level() default 1;
}
  • The repeteable annotation must be declared with @Repeteable and contain a value that refers to the containing type annotation.
  • The containing type annotation must include an element named value(), which is a primitive array of the repeteable annotation type.

Using Common Annotations

This set of annotations apply to various types and methods, and have special rules. If they’re used incorrectly, the compiler will report an error.

Marking Methods with @Override

It’s a marker annotation that’s used to indicate a method is overriding an inherited method.

The overriding method must have

  • the same signature
  • same or broader access modifier
  • a covariant return tyep
  • not declare any new or broader checked exceptions
public interface Intelligencce {
	int cunning();
}

public class Canine implements Intelligence {
	@Override
	public int cunning() {
		return 500;
	}
}

(!) During the exam, you should be able to identify anywhere this annotation is used incorrectly (!)

public class Dog extends Canine {
	@Override // DOES NOT COMPILE
	public boolean playFetch() {
		return true;
	}

	@Override // DOES NOT COMPILE
	void cunning(int timeOfDay) {
	}
}

Declaring interfaces with @FunctionalInterface

This marker annotation can be applied to any valid functional interface.

@FunctionalInterface
public interface Intelligence {
	int cunning();
}

The compiler will throw an error when applied to anything other than a valid functional interface.

@FunctionalInterface
abstract class Reptile { // DOES NOT COMPILE
	abstract String getName();
}

(!) check and be sure the it’s applied only to interfaces (!)

Retiring Code with @Deprecated

This notifiers users that a new version of the method is available and gives them time to migrate their code to the new version, before we finally remove the old version.

It supports two optional values since() and boolean forRemoval().

@Deprecated
public class ZooPlanner {
}
@Deprecated(since="1.8", forRemoval=true)
public class ZooPlanner {
}

Ignoring Warnings with @SuppressWarnings

While the compiler can be helpful in warning you of potential coding problems, sometimes you need to perform a particular operation. Applyin this annotation disables this warnings.

It takes two values.

Value Description
“deprecation” Ignore warnings related to types or methods marked with @Deprecated annotation
“unchecked” Ignore warnings related to the use of raw types, such as List
@SupressWarnings("deprecation")
public void wakeUp() {
	SongBird.sing(10);
}

@SupressWarnings("unchecked")
public void goToBed() {
	SongBird.chirp(new ArrayList());
}

Protecting Arguments with @SafeVarargs

It indicates that a method does not perform any potential unsafe operations on its varargs parameters. It can be applied only to constructors or methods that cannot be overriden.

The annotation it’s used to indicate to other developers that your method does not perform any unsafe operations.

public class NeverDoThis {
	final int thisIsUnsafe(List<Integer>... carrot) {
		Object[] stick = carrot;
		stick[0] = Arrays.asList("nope!");
		return carrot[0].get(0); // ClassCastException at runtime
	}

	public static void main(String[] a) {
		var carrot = new ArrayList<Integer>();
		new NeverDoThis().thisIsUnsafe(carrot);
	}
}

This code compiles, although it generates two compiler warnings.

[Line 4] Type safety: Potential heap pollution via varargs parameter carrot
[Line 11] Type safety: A generic array of List<Integer> is created for a varargs parameter

We can remove both compiler warnings by adding the @SafeVarargs annotation.

@SafeVarargs
final int thisIsUnsafe(List<Integer>... carrot) {

We didn’t fix the unsafe operation. It still throws ClassCastException. However, we made it so the compiler won’t warn us about it anymore.

(!) For the exam, you just need to be able to identify unsafe operations and know they often involve generics. You should also know the annotation cna be applied only to methods that contain a varargs parameter and are not able to be overriden. (!)

@SafeVarargs
public static void eat(int meal) {} // DOES NOT COMPILE (missing varargs)

@SafeVarargs
protected void drink(String... cup) {} // DOES NOT COMPILE (not static, final or private)

@SafeVarargs
void chew(boolean... food) {} // DOES NOT COMPILE (not static, final or private)

Summary

In this chapter, we taught you everything you need to know about annotations for the exam. Ideally, we also taught you how to create and use custom annotations in your daily programming life. As we mentioned early on, annotations are one of the most convenient tools available in the Java language.

For the exam, you need to know the structure of an annotation declaration, including how to declare required elements, optional elements, and constant variables. You also need to know how to apply an annotation properly and ensure required elements have values. You should also be familiar with the two shorthand notations we discussed in this chapter. The first allows you to drop the elementName under certain conditions. The second allows you to specify a single value for an array element without the array braces ({}).

You need to know about the various built-in annotations available in the Java language. We sorted these into two groups: annotations that apply to other annotations and common annotations. The annotationspecific annotations provide rules for how annotations are handled by the compiler, such as specifying an inheritance or retention policy. They can also be used to disallow certain usage, such as using a methodtargeted annotation applied to a class declaration.

The second set of annotations are common ones that you should know for the exam. Many, like @Override and @FunctionalInterface, are quite useful and provide other developers with additional information about your application.

Exam Essentials

Be able to declare annotations with required elements, optional elements, and variables. An annotation is declared with the @interface type. It may include elements and public static final constant variables. If it does not include any elements, then it is a marker annotation. Optional elements are specified with a default keyword and value, while required elements are those specified without one.

Be able to identify where annotations can be applied. An annotation is applied using the at (@) symbol, followed by the annotation name. Annotations must include a value for each required element and can be applied to types, methods, constructors, and variables. They can also be used in cast operations, lambda expressions, or inside type declarations.

Understand how to apply an annotation without an element name. If an annotation contains an element named value() and does not contain any other elements that are required, then it can be used without the elementName. For it to be used properly, no other values may be passed.

Understand how to apply an annotation with a single-element array. If one of the annotation elements is a primitive array and the array is passed a single value, then the annotation value may be written without the array braces ({}).

Apply built-in annotations to other annotations. Java includes a number of annotations that apply to annotation declarations. The @Target annotation allows you to specify where an annotation can and cannot be used. The @Retention annotation allows you to specify at what level the annotation metadata is kept or discarded. @Documented is a marker annotation that allows you to specify whether annotation information is included in the generated documentation. @Inherited is another marker annotation that determines whether annotations are inherited from super types. The @Repeatable annotation allows you to list an annotation more than once on a single declaration. It requires a second containing type annotation to be declared.

Apply common annotations to various Java types. Java includes many built-in annotations that apply to classes, methods, variables, and expressions. The @Override annotation is used to indicate that a method is overriding an inherited method. The @FunctionalInterface annotation confirms that an interface contains exactly one abstract method. Marking a type @Deprecated means that the compiler will generate a depreciation warning when it is referenced. Adding @SuppressWarnings with a set of values to a declaration causes the compiler to ignore the set of specified warnings. Adding @SafeVarargs on a constructor or private, static, or final method instructs other developers that no unsafe operations will be performed on its varargs parameter. While all of these annotations are optional, they are quite useful and improve the quality of code when used.

Java Modules

(the code for this is available at http://www.selikoff.net/ocp11-1

(do a more in-depth review of this chapter, if there’s enough time)

Introducing Modules

The Java Platform Module System (JPMS) was introduced in Java 9 to group code at a higher level. The main purpose of a module is to provide groups of related packages to offer a particular set of functionality to developers.
It’s like a JAR file except a developer chooses which packages are accessible outside the module.

Exploring a Module

A module is a group of one or more packages plus a special file called module-info.java.

(insert image of module)

Each block is a module. The arrows between them represent dependencies where one module relies on code in another.

(insert image of module)

Inside them, there’re three packages with two classes each. There’s also a module-info.java. This class is required to be inside all modules.

Benefits of Modules

Better Access Control

Aside from the existing access control (private, public…), they allow us to restrict access to just some packages, or implement complex access logic.
They act as a fifth level of access control. They can expose packages within the modular JAR to specific other packages (internal packages).

Clearer Dependency Management

It’s common for libraries to depend on other libraries. Usually you need to find this by reading documentation or project files.
In a fully modular environment, each project specifies its dependencies in a module-info.java file. If a dependency is missing, Java complains when launching the program.

Custom Java Builds

The JDK is way to big. The module system allows developers to specify what modules they actually need. This makes possible to create a smaller runtime image that’s customized to what the app needs and nothing more. Users can run that image without having Java installed at all.

The tool jlink is used to create this runtime image.

Improved Performance

Since Java now knows which modules are required, it only needs to look at those at class loading time. This improves startup time for big programs and requires less memory to run.

Unique Package Enforcement

It prevents the scenario of the same package being in two JARs. A package is only allowed to be supplied by one module.

Creating and Running a Modular Program

(insert image of module)

Creating the Files

Create the following classes and packages.

Task.java

package zoo.animal.feeding;

public class Task {
	public static void main(String[]args) {
		sout("All fed!");
	}
}

module-info.java

module zoo.animal.feeding {
}

Key differences between module-info.java and a regular Java class

  • This file must be in the root directory of your module. Regular Java classes should be in packages.
  • This file must use the keyword module instead of class.
  • Its name follows the naming rules for package names. It often includes periods in its name.

The file may be empty, but it won’t create a .class.

Directory structure

- mods
- feeding
	- module-info.java
	- zoo
		- animal
			- feeding
				- Task.java

The directory called mods (short for module) is for storing the module artifacts.

Compiling Our First Module

Before we can run modular code, we need to compile it.

javac --module-path mods
	-d feeding
	feeding/zoo/animal/feeding/*.java
	feeding/module-info.java

-d specifies the directory to place the class files in.
The syntax --module-path and -p are equivalent.

Running Our First Module

java --module-path feeding
	--module zoo.animal.feeding/zoo.animal.feeding.Task

The short version for --module is -m.

Packaging Out First Module

A module isn’t much use if we can run it only in the folder it was cerated in.

jar -cvf mods/zoo.animal.feeding.jar -C feeding/ .

Versioning

It’s possible to version your module using --module-version,

Updating Our Example For Multiple Modules

Updating the Feeding Module

We need to declare the intent of our modules calling each other inside module-info.
The exports keyword is used to indicate that a module intends for those packages to be used by Java code outside the module. Without it, a module is only available to be run from the command line on its own.

module zoo.animal.feeding {
	exports zoo.animal.feeding;
}

Recompiling and repackaging the module will update the module-info inside our zoo.animals.feeding.jar file.

javac --module-path mods
	-d feeding
	feeding/zoo/animal/feeding/*.java
	feeding/module-info.java

jar -cvf mods/zoo.animal.feeding.jar -C feeding/ .

Creating a Care Module

Let’s create the zoo.animal.care module. We’re going to have two packages. The zoo.animal.care.medical package will have the classes and methods that are intended for use by other modules. The zoo.animal.care.details package is only going ot be used by this module. It won’t be exported from the module.

(insert image of module)

package zoo.animal.care.details;

import zoo.animal.feeding.*;

public class HippoBirthday {
	private Task task;
}
package zoo.animal.care.medical;

public class Diet { }

This time the module-info.java specifies three things.

module zoo.animal.care {
	exports zoo.animal.care.medical;
	requires zoo.animal.feeding;
}
  • It specifies the name of the module.
  • It lists the package we are exporting so it can be used by other modules.
  • It specifies that a module is needed.

We now compile and package the module

javac -p mods
	-d care
	care/zoo/animal/care/details/*.java
	care/zoo/animal/care/medical/*.java
	care/module-info.java

Note that when compiling a module, order matters! module-info goes last.

Now that we have compiled code, it’s time to create a modular JAR.

jar -cvf mods/zoo.animal.care.jar -C care/ .

Creating the Talks Module

So far we’ve used only one exports and requires in a module. Now we’ll handle exporting multiple packages or requiring multiple modules.

module zoo.animals.talks {
	// allow other modules to reference all three packages
	exports zoo.animal.talks.content;
	exports zoo.animal.talks.media;
	exports zoo.aniaml.talks.schedule;

	// specifies two modules that this module depends on
	requires zoo.animal.feeding;
	requires zoo.animal.care;
}

Then we have the six classes:

package zoo.animal.talks.content;

public class ElephantScript {}
package zoo.animal.talks.content;

public class SeaLionScript {}
package zoo.animal.talks.media;

public class Announcement {
	public static void main(String[] args) {
		sout("we will be having talks");
	}
}
package zoo.animal.talks.media;

public class Signage {}
package zoo.animal.talks.schedule;

public class Weekday {}

We compile and build the module

javac -p mods
	-d talks
	talks/zoo/animal/talks/content/*.java
	talks/zoo/animal/talks/media/*.java
	talks/zoo/animal/talks/schedule/*.java
	talks/module-info.java

jar -cvf mods/zoo.animal.talks.jar -C talks/

Creating the Staff Module

This is the module-info

module zoo.staff {
	requires zoo.animal.feeding;
	requires zoo.animal.care;
	requires zoo.animal.talks;
}

We have a single class in this module

package zoo.staff;

public class Jobs {}

Compile & build

javac -p mods
	-d staff
	staff/zoo/staff/*.java
	staff/module-info.java

jar -cvf mods/zoo.staff.jar -C staff/

Diving into the module-info File

exports and requires are “keywords”, meaning that they’re only keywords inside a module-info.java file. In other files, like classes or interfaces, they’re free to be used. This kind of special “keyword” are called directives.

exports

exports packageName exports a package to other modules.
It’s also possible to export a package to a specific module.

module zoo.animal.talks {
	exports zoo.animal.talks.content to zoo.animal.staff;
	exports zoo.animal.talks.media;
	exports zoo.animal.talks.schedule;

	requires zoo.animal.feeding;
	requires zoo.animal.care;
}

From zoo.animal.staff module nothing has changed. However, no other modules would be allowed to access that package.

Exported Types

Exporting a package means, that all public classes, interfaces and enums are exported. Any public and protected fields and methods in those files are visible.

requires transitive

(check again: book 1. Page 528)

requires moduleName specifies that the current module depends on moduleName.
There’s also requires transitive moduleName, which means any module that requires this module will also depend on moduleName.

module zoo.animal.care {
	exports zoo.animal.care.medical;
	requires transitive zoo.animal.feeding;
}

Effects of requires transitive

(check again: book 1. Page 530)

Duplicate requires Statements

(!) One place the exam might try to trick you is mixing requires and requires transitive together. (!)

module bad.module {
	requires zoo.animal.talks;
	requires transitive zoo.animal.talks;
}

Thsi doesn’t compile, because Java doesn’t allow you to repeat the same module in a requires clause. It’s redundant. Keep in mind that requires transitive is like requires with some extra behaviour.

provides, uses, and opens

  • provides - specifies that a class provides an implementation of a service. It’s kind of a service as a fany interface. To use it you supply the API and class name that implements it.
provides zoo.staff.ZooApi with zoo.staff.ZooImpl
  • uses - specifies that a module is relying on a service. To code it you supply the API you want to call.
uses zoo.staff.ZooApi
  • opens - It specifies explicitly that developers are allowed to use reflection.
opens zoo.animal.talks.schedule;
opens zoo.animal.taslks.media to zoo.staff;

The first example allows any module using this one to use reflection.
The second one, gives that privilege only to the zoo.staff package.

Discovering Modules

Since Java 9, the classes built into the JDK were modularized as well.

The java Command

It has three module-related options.

Describing a Module

Suppose you’re given the zoo.animal.feeding module JAR file and want to know about its module structure.
You could unjar it and open the module-info. This would show you that the moduel exports one package and doesn’t require any modules.

module zoo.animal.feeding {
	exports zoo.animal.feeding;
}

There’s an easier way. The java command now has an option to describe a module.

This 2 commands are equivalent

java -p mods
    -d zoo.animal.feeding
java -p mods
	--describe-module zoo.animal.feeding

Each prints information about the module such as

zoo.animal.feeding file:///aboslutePath/mods/zoo.animal.feeding.jar
exports zoo.animal.feeding
requires java.base mandated

The first line is the module we asked about.
The second one, starts information about the module.
The third one, we see mandated as keyword. The java.base module is special. It’s automatically added as a dependency to all modules.

Listing Available Modules

List all modules that are available.

java --list-modules

the output are 70 lines that looked like this

java.base@11.0.2
java.compiler@11.0.2
java.datatransfer@11.0.2

This is a listing of all the modules that come with Java and their version numbers. Here 11.0.2 is the Java version.

This command may be used with custom code.

java -p mods --list-modules

Showing Module Resolution

In case listing modules didn’t give you enough output, you can also use --show-module-resolution. You can think of it as a way of debugging modules. It spits out a lot of output when the program starts up.

java --show-module-resolution
	-p feeding
	-m zoo.animal.feeding/zoo.animal.feeding.Task

The jar Command

It can describe a module, just like the java command.

Both of this commands are equivalent.

jar -f mods/zoo.animal.feeding.jar -d
jar --file mods/zoo.animal.feeding.jar --describe-module

The jar version includes the module-info in the filename.

You just need to know that both commands can describe a module.

The jdeps Command

It gives you information about dependencies within a module. Unlike describing a module, it looks at the code in addition to the module-info file. This tells you what dependencies are actually used rather than simply declared.

Both of these commands give the same output.

jdeps -s mods/zoo.animal.feeding.jar
jdeps -summary mods/zoo.animal.feeding.jar

(review this again more in depth. book 1 page 536)

The jmod Command

JMOD files are recommended only when you have native libraries or something can’t go inside a JAR file.

jmod is only for working with the JMOD files.

The following table are JMOD’s modes or syntax

operation description
create creates a JMOD file
extract extracts all files frmo the JMOD. Works like unzipping.
describe prints the module details such as requires
list lists all files in the JMOD file
hash shows a long string that goes with the file

Reviewing Command-Line Options

This table shows the command lines you should expect to encounter on the exam.

Description Syntax
Compile nonmodular code javac -cp classpath -d directory classesToCompile
  javac –class-path classpath -d directory classesToCompile
  javac -classpath classpath -d directory classesToCompile
Run nonmodular code java -cp classpath package.className
  java –classpath classpath package.className
  java –class-path classpath package.className
Compile a module javac -p moduleFOlderName -d directory classesToCompileIncludingModuleInfo
  javac –module-path moduleFOlderName -d directory classesToCompileIncludingModuleInfo
Run a module java -p moduleFolderName -m moduleName/package.className
  java –module-path moduleFolderName –module moduleName/package.className
Describe a module java -p moduleFolderName -d moduleName
  java –module-path moduleFolderName –describe-module moduleName
  jar –file jarName –describe-module
  jar -f jarName -d
List available modules java –module-path moduleFolderName –list-modules
  java -p moduleFolderName –list-modules
  java –list-modules
View dependencies jdeps -summary –module-path moduleFolderName jarName
  jdeps -s –module-path moduleFolderName jarName
Show module resolution java –show-module-resolution -p moduleFolderName -d moduelName
  java –show-module-resolution –module-path moduleFolderName –describe-module moduleName

javac modifiers

|Modifier|Description| |:—:|:—:| |-cp classpath|location of JARs in a nonmodular program| |-classpath classpath|| |–class-path classpath|| |-d dir|directory to place generated class files| |-p path|location of JARs in a modular program| |–module-path path||

java modifiers

|Modifier|Description| |:—:|:—:| |-p path|location of JARs in a modular program| |–module-path path|| |-m name|module name to run| |–module name|| |-d|describes the details of a module| |–describe-module|| |–list-modules|list observable modules without running a program| |–show-module-resolution|shows modules when running a program|

jar modifiers

|Modifier|Description| |:—:|:—:| |-c| craete a new JAR file| |–create|| |-v|prints details when working with JAR files| |–verbose|| |-f|JAR filename| |–file|| |-C|Directory containing giles to be used to create the JAR| |-d|Describes the details of a module| |–describe-module||

jdeps modifiers

|Modifier|Description| |:—:|:—:| |–module-path path|Location of JARs in a modular program| |-s|summarizes output| |-summary||

Summary

The Java Platform Module System organizes code at a higher level than packages. Each module contains one or more packages and a module-info file. Advantages of the JPMS include better access control, clearer dependency management, custom runtime images, improved performance, and unique package enforcement.

The process of compiling and running modules uses the –module-path, also known as -p. Running a module uses the –module option, also known as -m. The class to run is specified in the format moduleName/className.

The module-info file supports a number of keywords. The exports keyword specifies that a package should be accessible outside the module. It can optionally restrict that export to a specific package. The requires keyword is used when a module depends on code in another module. Additionally, requires transitive can be used when all modules that require one module should always require another. The provides and uses keywords are used when sharing and consuming an API. Finally, the opens keyword is used for allowing access via reflection.

Both the java and jar commands can be used to describe the contents of a module. The java command can additionally list available modules and show module resolution. The jdeps command prints information about packages used in addition to module-level information. Finally, the jmod command is used when dealing with files that don’t meet the requirements for a JAR.

Exam Essentials

Identify benefits of the Java Platform Module System. Be able to identify benefits of the JPMS from a list such as access control, dependency management, custom runtime images, performance, and unique package enforcement. Also be able to differentiate benefits of the JPMS from benefits of Java as a whole. For example, garbage collection is not a benefit of the JPMS.

Use command-line syntax with modules. Use the command-line options for javac, java, and jar. In particular, understand the module (-m) and module path (-p) options.

Create basic module-info files. Place the module-info.java file in the root directory of the module. Know how to code using exports to expose a package and how to export to a specific module. Also, know how to code using requires and requires transitive to declare a dependency on a package or to share that dependency with any modules using the current module.

Identify advanced module-info keywords. The provides keyword is used when exposing an API. The uses keyword is for consuming an API. The opens keyword is for allowing the use of reflection.

Display information about modules. The java command can describe a module, list available modules, or show the module resolution. The jar command can describe a module similar to how the java command does. The jdeps command prints details about a module and packages. The jmod command provides various modes for working with JMOD files rather than JAR files.

Exceptions

Understanding Exceptions

Understanding Exception Types

An exception is an event that alters program flow. There’s a Throwable superclass for all objects that represent these events.

The class Error is for when something goes so horribly wrong that your program should not attempt to recover from it. From example, the disk drive has disappeared or the program runs out of memory.

Throwable is the parent class of all exceptions, including Error.

Checked Exceotions

A checked exception is an exception that must be declared or handled by the application code where it’s thrown. They all inherit from Exception, but not from Error or RuntimeException.
They tend to be more anticipated - from example, trying to read a file that doesn’t exist.

The handle or declare rule means that all checked exceptions that could be thrown within a method, are either wrapped in compatible try and catch blocks, or declared in the method signature.

void fall(int distance) throws IOException {
	if(distance > 10) {
		throw new IOException();
	}
}

The throw keyword tells Java that you want to throw an Exception.
The throws keyword simply declares that the method might throw an Exception. It also might not.

To handle the exception

void fall(int distance) {
	try {
		if(distance > 10) {
			throw new IOException();
		}
	} catch(Exception e) {
		e.printStackTrace();
	}
}

Unchecked Exceptions

An unchecked exception is any exception that does not need to be declared or handled by the application code where it’s thrown. They’re often referred to as runtime exceptions. They include any class that inherits from RuntimeException or Error.
They tend to be unexpected, but not necessarily fatal. For example, NullPointerException.

void fall(String input) {
	sout(input.toLowerCase());
}

void(null);

Throwing an Exception

On the exam, you will see two types of code that result in an exception.

The first is code that’s wrong

// throws ArrayIndexOutOfBoundsException
String[] animals = new String[0];
sout(animals[0]);

(!) Pay special attention to code that calls a method on a null reference or that references an invalid array or List index. (!)

The second way is to explicitly request to throw the Exception.

// all these are valid
throw new Exception();
throw new Exception("Ow! I fell. ");
throw new RuntimeException();
throw new RuntimeException("Ow! I fell. ");

(!) Anytime you see throw or throws, make sure the correct one is being used. (!)
throw is used to throw a new exception
throws is used only at the end of a method declaration.

An Exception is an Object. You can store it in a variable.

Exception e = new RuntimeException();
throw e;

They need to be instantiated

throw RuntimeException(); // DOES NOT COMPILE

Be careful with unreacheable code.

try {
	throw new RuntimeException();
	throw new ArrayIndexOutOfBoundsException(); // DOES NOT COMPILE!
} catch(Exception ex) {
}

Recognizing Exception Classes

RuntimeException Classes

RuntimeException and its subclasses are unchecked exceptions that don’t have to be handled or declared. They can be thrown by the programmer or by the JVM.

  • ArithmeticException - Thrown when code attempts to divide by zero
  • ArrayIndexOutOfBoundsException - code uses an illegal index to access an array
  • ClassCastException - an attempt is made to cast an object to a class of which is not an instance
  • NullPointerException - there’s a null reference where an object is required
  • IllegalArgumentException - thrown by the programmer to indicate that a method has been passed an illegal or inappropiate argument
  • NumberFormatException - Subclass of IllegalArgumentException. When an attempt is made to convert a String to a numeric type, but it doesn’t have an appropiate format

Checked Exception Classes

They have Exception in their hierarchy but not RuntimeException. They must be handled or declared.

  • IOException - thrown programmatically when there’s a problem reading or writing a file.
  • FileNotFoundException - subclass of IOException. thrown when code tries to reference a file that does not exist.

Error Classes

Errors are unchecked exceptions that extend from Error. They’re thrown by the JVM and should not be handled or declared. They’re rare.

  • ExceptionInInitializerError - Thrown when a static initializer throws an exception and doesn’t handle it.
static {
	int[] countsOfMoose = new int[3];
	int num = countsOfMoose[-1];
}
  • StackOverflowError - a method calls itself too many times (infinite recursion).
  • NoClassDefFoundError - a class that the code uses is available at compile time but not at runtime.

Handling Exceptions

Using try and catch Statements

Java uses a try statement to separate the logic that might throw an exception, from the logic to handle that exception.

The curly braces are required for try and catch blocks.

try {

} catch(Exception ex) {

}

Some invalid try statements

try // DOES NOT COMPILE
	fall();
catch(Exception ex)
	sout();
try { // DOES NOT COMPILE
	fall();
}

Chaining catch Blocks

First, you must be able to recognize if the exception is a checked or an unchecked exception.
Second, you need to determine whether any of the exceptions are subclasses of the others.

// unchecked exceptions
class AnimalsOutForAWalk extends RuntimeException { }
class ExhibitClosed extends RuntimeException { }
class ExhibitClosedForLunch extends ExhibitClosed { }
public void visitPorcupine() {
	try {
		seeAnimal();
	} catch (AnimalsOutForAWalk e) { // first catch block
		System.out.print("try back later");
	} catch (ExhibitClosed e) { // second catch block
		System.out.print("not today");
	}
}

A rule exists for the order of the catch blocks. Java looks at them in the order they appear. If it’s impossible for one of the catch blocks to be executed, a compiler error about unreachable code occurs.
(!) This happens when a superclass catch block appears before a subclass catch block. Pay attention to any subclass exceptions. (!)

public void visitMonkeys() {
	try {
		seeAnimal();
	} catch(ExhibitClosedForLunch e) { // subclass
		sout("try back later");
	} catch(ExhibitClosed e) { // superclass
		sout("not today");
	}
}

If the order was reversed, they wouldn’t compile as the second one couldn’t ever be reached.

public void visitMonkeys() {
	try {
		seeAnimal();
	} catch(ExhibitClosed e) {
		sout("try back later");
	} catch(ExhibitClosedForLunch e) { // DOES NOT COMPILE
		sout("not today");
	}
}

Applying a Multi-catch Block

If we want to apply the same result to multiple exceptions, we may re-use code.

public static void main(String args[]) {
	try {
		soutln(Integer.parseInt(args[1]));
	} catch(ArrayIndexOutOfBoundsException | NumberFormatException ex) {
		sout("Missing or invalid input");
	}
}

(!) The exam might try to trick you with invalid syntax (!)

catch(Exception1 e | Exception2 e) // DOES NOT COMPILE
catch(Exception1 e1 | Exception2 e2) // DOES NOT COMPILE
catch(Exception1 | Exception2 e) // COMPILES

Multi-cath is intented to be used for exceptions that aren’t related, and it prevents you from specifying redundant types in a multi-catch.

try {
	throw new IOException();
} catch(FileNotFoundException | IOException p) {} // DOES NOT COMPILE

This fails, because FileNotFoundException is already caught by the alternative IOException.
The Exceptions’ order doesn’t matter inside a single block.
You can’t list the same exception type more than once in the same try statement.
The more general superclasses must be caught after their subclasses.

Adding a finally Block

The try statement also lets you run code at the end with a finally clause, regardless of whether an exception is thrown.

try {
	seeAnimals();
	fall();
} catch(Exception e) {
	getHug();
} finally {
	seeMoreAnimals();
}

If an exception is thrown, the finally block is run after the catch block. If no exception is thrown, the finally block is run after the try block completes.

The exam will try to trick you with missing clasuses, or clasuse in the wrong order.

try { // DOES NOT COMPILE
	fall();
} finally {
	sout("all better");
} catch(Exception e) {
	sout("get up");
}

try { // DOES NOT COMPILE
	fall();
}

try { // COMPILES
	fall();
} finally {
	sout("all better");
}

The catch block is not required if finally is present.

StringBuilder sb = new StringBuilder();
try {
	sb.append("t");
} catch(Exception e) {
	sb.append("c");
} finally {
	sb.append("f");
}

sb.append("a");
sout(sb.toString());

This will print tfa.

If there’s a finally block, this will always be executed.

int goHome() {
	try {
		sout("1");
		return -1;
	} catch(Exception e) {
		sout("2");
		return -2;
	} finally {
		sout("3");
		return -3;
	}
}

This will print 13 and it will always return -3.

There’s one exception to this: System.exit(0).

try {
	System.exit(0);
} finally {
	sout("Never going to get here"); // not printed.
}

Finally Closing Resources

It avoids resource leaks by closing resources.

public void readFile(String file) {
	try (FileInputStream is = new FileInputStream("myfile.txt")) {
		// do something
	} catch(IOException e) {
		e.printStackTrace();
	}
}

As soon as a connection passes out of scope, Java will attempt to close it.

Behind the scenes, the compiler replaces this try-with-resources block for a try with a finally block. You can still create a programmer defined finally block, just be aware that the implicit one will be called first.

Basics of Try-with-Resources

One or more resources can be opened in the try clause. When there’re multiple resources opened, they’re closed in the reverse order from which they were created.

try (FileInputStream in = new FileInputStream("data.txt");
		FileOutputStream out = new FileOutputStream("output.txt")) {

}

The catch block is optional in try-with-resources!

You can’t put any random class inside the try-with-resources statement. Java requires a class which implements AutoCloseable interface, which includes a void close() method.

Declaring Resources

While try-with-resources does support declaring multiple variables, each variable must be declared in a separate statement.

try (MyFileClass is = new MyFileClass(1), // DOES NOT COMPILE
		os = new MyFileClass(2)) {

}
try (MyFileClass ab = new MyFileClass(1),
		MyFileClass cd = new MyFileClass(2)) { // DOES NOT COMPILE

}

You can declare a resource using var as the data type.

try (var f = new FileInputStream("it.txt")) {
	// process file
}

Scope of Try-with-Resources

The resources created are in scope only within the try block.

try (Scanner s = new Scanner(System.in)) {
	s.nextLine();
} catch(Exception e) {
	s.nextLine(); // DOES NOT COMPILE
} finally {
	s.nextLine(); // DOES NOT COMPILE
}

The problem is that Scanner has gone out of scope. You can’t accidentally use an object that has been closed.

Following Order of Operation

  • Resources are closed after the try clause ends and before any catch/finally clauses.
  • Resources are closed in the reverse order from which they were created.
public class MyFileClass implements AutoCloseable {
	private final int num;

	public MyFileClass(int num) { this.num = num; }

	public void close() {
		sout("Closing: " + num);
	}
}

public static void main(String... xyz) {
	try(MyFileClass a1 = new MyFileClass(1);
			MyFileClass a2 = new MyFileClass(2)) {
		throw new RuntimeException();		
	} catch (Exception e) {
		sout("ex");
	} finally {
		sout("finally");
	}
}

Since the resources are closed in reverse order, this prints

Closing: 2
Closing: 1
ex
finally

Throwing Additional Exceptions

There’re situations where we need to catch an exception inside of a catch or finally block.

public static void main(String[] a) {
	FileReader reader = null;
	try {
		reader = read();
	} catch(IOException ex) {
		try {
			if(reader != null)
				reader.close();
		} catch (IOException inner) {
			//
		}
	}
}

private static FileReader read() throws IOException {
	//
}

The following example shows that only the last exception to be thrown matter.

try {
	throw new RuntimeException();
} catch(RuntimeException ex) {
	throw new RuntimeException();
} finally {
	throw new Exception();
}

Another example

public String exceptions() {
	StringBuilder result = new StringBuilder();
	String v = null;

	try {
		try {
			result.append("before_");
			v.length();
			result.append("after_");
		} catch(NullPointerException e) {
			result.append("catch_");
			throw new RuntimeException();
		} finally {
			result.append("finally_");
			throw new Exception();
		}
	} catch(Exception e) {
		result.append("done");
	}
	return result.toString();
}

This prints before_catch_finally_done

Calling Methods That Throw Exceptions

When you’re calling a method that throws an exception, the rules are the same as within a method.

class NoMoreCarrotsException extends Exception {}

public class Bunny {
	private static void eatCarrot() throws NoMoreCarrotsException {
	}

	public static void main(String[] args) {
		eatCarrot(); // DOES NOT COMPILE
	}
}

Checked exceptions must be handled or declared. The solution would be either of this.

public static void main(String[] args)
	throws NoMoreCarrotsException {
	eatCarrot();
}
public static void main(String[] args) {
	try {
		eatCarrot();
	} catch(NoMoreCarrotsException ex) {
		//
	}
}

The reverse is also true, we cannot try to catch an exception when a method doesn’t include it in its declaration.

private void eatCarrot() {}

public void bad() {
	try {
		eatCarrot();
	} catch(NoMoreCarrotsException ex) { // DOES NOT COMPILE
		// ex
	}
}

(!) When you see a checked exception declared inside a catch block on the exam, check and make sure the code in the associated try block is capable of throwing the exception or a subclass of the exception. If it’s not capable, the code is unreachable and does not compile.
Remember that this rules does not extend to unchecked exceptions or exceptions declared in a method signature. (!)

Declaring and Overriding Methods with Exceptions

When a class overrides a method from a superclass or implements a method from an interface, it’s not allowed to add new checked exceptions to the method signature.

class CanNotHopException extends Exception { }

class Hopper {
	public void hop() {}
}

class Bunny extends Hopper {
	public void hop() throws CanNotHopException {} // DOES NOT COMPILE
}

An overriden method in a subclass is allowed to declare fewer exceptions than the superclass or interface. This is legal because callers are already handling them.

class Hopper {
	public void hop() throws CanNotHopException { }
}

class Bunny extends Hopper {
	public void hop() { }
}

Similarly, a class is allowed to declare a subclass of an exception type.

class Hopper {
	public void hop() throws Exception {}
}

class Bunny extends Hopper {
	public void hop() throws CanNotHopException {}
}

The following code is legal because it has an unchecked exception in the subclass.

class Hopper {
	public void hop() {}
}

class Bunny extends Hopper {
	public void hop() throws IllegalStateException {}
}

The declaration is redundant. Methods are free to throw any unchecked exceptions they want without mentioning them in the method declaration.

Printing an Exception

There’re three ways to print an exception.

  • You can let Java print it out
  • Print just the message
  • Print where the stack trace comes from
private static void hop() {
	throw new RuntimeException("cannot hop");
}

public static void main(String[] args) {
	try {
		hop();
	} catch(Exception ex) {
		sout(ex);
		sout(ex.getMessage());
		ex.printStackTrace();
	}
}

They result in the following output

java.lang.RuntimeException: cannot hop
cannot hop
java.lang.RuntimeException: cannot hop
	at Handling.hop(Handling.java:15)
	at Handling.main(Handling.java:7)

Summary

An exception indicates something unexpected happened. A method can handle an exception by catching it or declaring it for the caller to deal with. Many exceptions are thrown by Java libraries. You can throw your own exceptions with code such as throw new Exception().

All exceptions inherit Throwable. Subclasses of Error are exceptions that a programmer should not attempt to handle. Classes that inherit RuntimeException and Error are runtime (unchecked) exceptions. Classes that inherit Exception, but not RuntimeException, are checked exceptions. Java requires checked exceptions to be handled with a catch block or declared with the throws keyword.

A try statement must include at least one catch block or a finally block. A multicatch block is one that catches multiple unrelated exceptions in a single catch block. If a try statement has multiple catch blocks chained together, at most one catch block can run. Java looks for an exception that can be caught by each catch block in the order they appear, and the first match is run. Then execution continues after the try statement. If both catch and finally throw an exception, the one from finally gets thrown.

A try-with-resources block is used to ensure a resource like a database or a file is closed properly after it is created. A try-with-resources statement does not require a catch or finally block but may optionally include them. The implicit finally block is executed before any programmer-defined catch or finally blocks.

RuntimeException classes you should know for the exam include the following:

  • ArithmeticException
  • ArrayIndexOutOfBoundsException
  • ClassCastException
  • IllegalArgumentException
  • NullPointerException
  • NumberFormatException

IllegalArgumentException is typically thrown by the programmer, whereas the others are typically thrown by the standard Java library.

Checked Exception classes you should know for the exam include the following:

  • IOException
  • FileNotFoundException

Error classes you should know for the exam include the following:

  • ExceptionInInitializerError
  • StackOverflowError
  • NoClassDefFoundError

For the exam, remember that NumberFormatException is a subclass of IllegalArgumentException, and FileNotFoundException is a subclass of IOException. When a method overrides a method in a superclass or interface, it is not allowed to add checked exceptions. It is allowed to declare fewer exceptions or declare a subclass of a declared exception. Methods declare exceptions with the keyword throws.

Exam Essentials

Understand the various types of exceptions. All exceptions are subclasses of java.lang.Throwable. Subclasses of java.lang.Error should never be caught. Only subclasses of java.lang.Exception should be handled in application code.

Differentiate between checked and unchecked exceptions. Unchecked exceptions do not need to be caught or handled and are subclasses of java.lang.RuntimeException and java.lang.Error. All other subclasses of java.lang.Exception are checked exceptions and must be handled or declared.

Understand the flow of a try statement. A try statement must have a catch or a finally block. Multiple catch blocks can be chained together, provided no superclass exception type appears in an earlier catch block than its subclass. A multi-catch expression may be used to handle multiple exceptions in the same catch block, provided one exception is not a subclass of another. The finally block runs last regardless of whether an exception is thrown.

Be able to follow the order of a try-with-resources statement. A try-with-resources statement is a special type of try block in which one or more resources are declared and automatically closed in the reverse order of which they are declared. It can be used with or without a catch or finally block, with the implicit finally block always executed first.

Identify whether an exception is thrown by the programmer or the JVM. IllegalArgumentException and NumberFormatException are commonly thrown by the programmer. Most of the other unchecked exceptions are typically thrown by the JVM or built-in Java libraries.

Write methods that declare exceptions. The throws keyword is used in a method declaration to indicate an exception might be thrown. When overriding a method, the method is allowed to throw fewer or narrower checked exceptions than the original version.

Recognize when to use throw versus throws. The throw keyword is used when you actually want to throw an exception—for example, throw new RuntimeException(). The throws keyword is used in a method declaration.

Advanced Class Design

Creating Abstract Classes

Introducing Abstract Classes

A subclass can override an inherited method defined in a parent class. Overriding a method potentially changes the behavior of a method in the parent class.

We use abstract classes when we want to define a class that other developers can extend and use, but you want them to specify particular types.
We can ensure that every class that extends it is required to provide its own overriden version.

An abstract class is one that cannot be instantiated and may contain abstract methods.
An abstract method is one that does not define an implementation when it’s declared.
Both of them are denoted with the abstract keyword.

abstract class Bird {
	public abstract String getName();

	public void printName() {
		sout(getName());
	}
}

public class Stork extends Bird {
	public String getName() { return "Stork!"; }

	public static void main(String[] args) {
		new Stork().printName();
	}
}

The class Stork must now override abstract getName(). The following implementation without it, wouldn’t compile.

public class Stork extends Bird {} // DOES NOT COMPILE

An abstract class is most commonly used when you want another class to inherit properties of a particular class, but you want the subclass to fill in some of the implementation details.

Defining Abstract Methods

An abstract class may include non-abstract methods. It may also include all kind of members such as variables, static, inner classes, constructors…

An abstract class it not required to include abstract methods. The following code compiles.

public abstract class Llama {
	public void chet() {}
}
// this is also fine
abstract public class Llama {
	public void chet() {}
}
abstract public class Llama {
	// this is also fine
	abstract public void chet() {}
}

The modifier cannot be placed after the class keyword nor after the return type in a method.

public class abstract Jackal { // DOES NOT COMPILE
	public int abstract howl(); // DOES NOT COMPILE
}

An abstract method can only be defined in an abstract class, or an interface.

public class Egret { // DOES NOT COMPILE
	public abstract void peck();
}

(!) If you see a class that contains an abstract method, make sure the class is marked as abstract. This modifier can be placed before or after the access modifier. (!)

It’s also not possible to define an abstract method that has a body, or default implementation. As long as you don’t mark the method as final, the subclass has the option to override an inherited method.

Constructors in Abstract Classes

abstract classes cannot be instantiated, but they’re still able to be initialized through constructors by their subclasses.

abstract class Bear {
	abstract CharSequence chew();

	public Bear() {
		sout(chew());
	}
}

public class Panda extends Bear {
	String chew() { return "yummy!"; }

	public static void main(String... args) {
		new Panda();
	}
}

This will print yummy! on runtime.

The main difference between constructors in an abstract class and a non-abstract one, is that the constructor in the abstract class can be called only when it’s being initialized by a non-abstract subclass. This makes sense, as abstract classes cannot be instantiated.

Invalid Abstract Method Declarations

Why don’t compile any of the following methods?

public abstract class Turtle {
	public abstract long eat()
	public abstract void swim() {};
	public abstract int getAge() { return 10; }
	public void sleep;
	public void goInShell();
}

eat() does not compile because it’s missing ;
swim() and getAge() are marked as abstract but implement a body for the method. An abstract method cannot contain an implementation.
sleep is missing parentheses.
goInShell() is not marked as abstract, but then it has to implement a body.

(!) If you come across a question on the exam in which a class or method is marked as abstract, make sure the class is properly implemented before attempting to solve the problem. (!)

Invalid Modifiers

We review the abstract modifier and which modifiers it’s not compatible with.

abstract and final Modifiers

If you mark something abstract, you’re intending for someone else to extend or implement it.
If you mark something final, you’re preventing anyone from extending or implementing it. These concepts are in direct conflict with each other.

public abstract final class Tortoise { // DOES NOT COMPILE
	public abstract final void walk(); // DOES NOT COMPILE
}

abstract and private Modifiers

A method cannot be marked both as abstract and private. How would you define a subclass that implements a required method if the method is not inherited by the subclass? You can’t.

public abstract class Whale {
	private abstract void sing(); // DOES NOT COMPILE
}

public class HumpbackWhale extends Whale {
	private void sing() {
		sout("X");
	}
}

The sing() method defined in the parent is not visible to the subclass.

abstract and static Modifiers

A static method cannot be overriden. It’s defined as belonging to the class, not an instance of the class. If it cannot be overriden, then it also cannot be marked abstract since it could never be implemented.

abstract class Hippopotamus {
	abstract static void swim(); // DOES NOT COMPILE
}

Creating a Concrete Class

A concrete class is a non-abstract class. The first concrete subclass that extends an abstract class is required to implement all inherited abstract methods.

(!) When you see a concrete class extending an abstract class on the exam, check to make sure that it implements all of the required abstract methods. (!)

public abstract class Animal {
	public abstract String getName();
}

public class Walrus extends Animal { // DOES NOT COMPILE
}

We highlight the first concrete subclass for a reason. An abstract class can extend a non-abstract class, and vice versa. Any time a concrete class is extending an abstract class, it must implement all of the methods that are inherited as abstract.

This compiles fine.

abstract class Mammal {
	abstract void showHorn();
	abstract void eatLeaf();
}

abstract class Rhino extends Mammal {
	void showHorn() {}
}

public class BlackRhino extends Rhino {
	void eatLeaf() {}
}

Reviewing Abstract Class Rules

For the exam you should know the following rules.

#### Abstract Class Definition Rules

  1. Abstract classes cannot be instantiated.
  2. All top-level types, including abstract classes, cannot be marked protected or private.
  3. Abstract classes cannot be marked final.
  4. Abstract classes may include zero or more abstract and nonabstract methods.
  5. An abstract class that extends another abstract class inherits all of its abstract methods.
  6. The first concrete class that extends an abstract class must provide an implementation for all of the inherited abstract methods.
  7. Abstract class constructors follow the same rules for initialization as regular constructors, except they can be called only as part of the initialization of a subclass.

Abstract Method Definition Rules

  1. Abstract methods can be defined only in abstract classes or interfaces.
  2. Abstract methods cannot be declared private or final.
  3. Abstract methods must not provide a method body/implementation in the abstract class in which they’re declared.
  4. Implementing an abstract method in a subclass follows the same ruels for overriding a method, including covariant return types, exception declarations etc.

Implementing Interfaces

Java allows a class to implement any number of interfaces. An interface is an abstract data type that declares a list of abstract methods that any class implementing the interface must provide. It can also include constant variables.
Both abstract methods and constant variables included with an interface are implicitly assumed to be public.

With Java 8, interfaces were updated to include static and default methods. A default method is one in which the interface method has a body and is not marked abstract. It was added for backward compatibility.

In Java 9, interfaces were updated to support private and private static methods. Both of these types were added for code reusability.

Defining an interface

Interface variables are referred to as constants, because they’re assumed to be public, static, and final. They’re initialized with a constant value when they’re declared.

Interfaces are not required to define any method.

public abstract interface WalksOnTwoLegs {}

An interface cannot be instantiated.

var e = new WalksOnTwoLegs(); // DOES NOT COMPILE

Interfaces cannot be marked as final.

public final interface WalksOnEightLegs {} // DOES NOT COMPILE

How do you use an interface?

interface Climb {
	Number getSpeed(int age);
}

public class FieldMouse implements Climb {
	public Float getSpeed(int age) {
		return 11f;
	}
}

The access modifier of the interface method is assumed to be public, although the concrete class must explicitly declare it.

If any of the interfaces implemented define abstract methods, then the concrete class is required to override them.

Like a class, an interface can extend another interface using extends. An interface, unlike a class, may extend multiple interfaces at once (a class may only extend one class).

interface Nocturnal {
	public int hunt();
}

interface CanFly {
	public void flap();
}

interface HasBigEyes extends Nocturnal, CanFly {}

Interfaces, unlike abstract classes, do not contain constructors and are not part of instance initialization. Interfaces simply define a set of rules that a class implementing them must follow.

  • A Java file may have at most one public top-level class or interface
  • A top-level class or interface can only be declared with public or package-private access.

What about Enums?

An enum is a specialized type that defines a set of fixed values. It’s declared with enum.

public enum Color {
	RED, YELOW, BLUE, GREEN;
}

Like classes and interfaces, enums can have more complex formations including methods, private constructors and instance variables.

Inserting Implicit Modifiers

An implicit modifier is one that the compiler will automatically insert.

  • Interfaces are assumed to be abstract.
  • Interface variables are assumed to be public, static, and final.
  • Interface methods without a body are assumed to be abstract, and public.

The following two interface definitions are equivalent, as the compiler will convert them both.

public interface Soar {
	int MAX_HEIGHT = 10;
	final static boolean UNDERWATER = true;
	void fly(int speed);
	abstract void takeoff();
	public abstract double dive();
}
public interface Soar {
	public static final int MAX_HEIGHT = 10;
	public final static boolean UNDERWATER = true;
	public abstract void fly(int speed);
	public abstract void takeoff();
	public abstract double dive();
}

Conflicting Modifiers

If a developer wants to mark a method or variable with an invalid modifier such as private or protected, it won’t compile, as the compiler will apply the public modifier to both.

public interface Dance {
	private int count = 4; // DOES NOT COMPILE
	protected void step(); // DOES NOT COMPILE
}

What about package-private? When working with interface members, the lack of access modifier always indicates public access, unlike classes.

(!) Spot compiler errors here (!)

1: private final interface Crawl {
2: 	String distance;
3: 	private int MAXIMUM_DEPTH = 100;
4: 	protected abstract boolean UNDERWATER = false;
5: 	private void dig(int depth);
6: 	protected abstract double depth();
7: 	public final void surface();
}

Line 1 doesn’t compile because it’s marked final, which cannot be applied to an interface. It’s also marked as private, which conflicts for top-level interfaces.
Line 2, because distance is not initialized.
Line 3 and 4, because they’re assumed to be public. Line 4 also again because variables cannot be marked abstract.
Line 5 and 6, because all interface abstract methods are assumed to be public.
Line 7, because it’s marked as final and methods without a body are assumed to be abstract, so it cannot be final.

Differences between Interfaces and Abstract Classes

Even though both are considered abstract types, only interfaces make use of implicit modifiers.

abstract class Husky {
	abstract void play();
}

interface Poodle {
	void play();
}

Both of these definitions are considered abstract, although Husky will not compile if play() is not marked as abstract, whereas the method in Poodle will compile fine either way.
They also don’t have the same access level. play() from Husky is considered package-private, whereas play() from Poodle is assumed to be public.

class Webby extends Husky {
	void play() {}
}

class Georgette implements Poodle {
	void play() {}
}

The class Webby compiles fine, but Georgette does not as it breaks overriding rules. It reduces the visibility from the method play() from public in Poodle to package-private in Georgette.

Inheriting an Interface

An interface can be inherited as

  • An interface can extend another interface.
  • A class can implement an interface.
  • A class can extend another class whose ancestor implements an interface.

When an interface is inherited, all of the abstract methods are inherited. If the type inheriting the interface is also abstract, then it’s not required to implement the interface methods. The first concrete subclass that inherits the interface must implement all of the inherited abstract methods.

Mixing Class and Interface Keywords

(!) The exam creators are fond of questions that mix class and interface terminology. (!)

Altough a class can implement an interface, a class cannot extend an interface. Likewise, an interface cannot implement another interface.

public interface CanRun{}
public class Cheetah extends CanRun {} // DOES NOT COMPILE

public class Hyena {}
public interface HasFur extends Hyena {} // DOES NOT COMPILE

Duplicate Interface Method Declarations

A class may inherit from two interfaces that contain the same abstract method.

public interface Herbivore {
	public void eatPlants();
}

public interface Omnivore {
	public void eatPlants();
	public void eatMeat();
}

public class Bear implements Herbivore, Omnivore {
	public void eatMeat() {
		sout("eating meat");
	}

	public void eatPlants() {
		sout("eating plants");
	}
}

You just need to be able to create a single method that overrides both inherited abstract methods at the same time.

If they have different method signatures it’s also not a problem, as it’s considered method overloading.

The methods are duplicates, but they’re also considered compatible. The compiler can resolve the differences between the two declarations without finding any conflicts.

If the duplicate methods have the same signature, but different return types, you need to implement it with return types that are covariant.

interface Dances {
	String swingArms();
}

interface EatsFish {
	CharSequence swingArms();
}

public class Penguin implements Dances, EatsFish {
	// covariant return type
	public String swingArms() {
		return "Swing!";
	}
}

If the return type is not covariant it won’t compile.

interface Dances {
	int countMoves();
}

interface EatsFish {
	boolean countMoves();
}

public class Penguin implements Dances, EatFish { // DOES NOT COMPILE

}

The same happens for a class or an interface.

interface LongEars {
	int softSkin();
}

interface LongNose {
	void softSkin();
}

// DOES NOT COMPILE
interface Donkey extends LongEars, LongNose {}

// DOES NOT COMPILE
abstract class Aardvark implements LongEars, LongNose {}

Polymorphism and Interfaces

Abstract Reference Types

When working with abstract types, you may prefer to work with the abstract reference type, rather than the concrete class.

import java.util.*;
public class Zoo {
	public void sortAndPrintZooAnimals(List<String> animals) {
		Collections.sort(animals);
		for(String a : animals) {
			sout(a);
		}
	}
}

The input may be any type of list, such as ArrayList or LinkedList instead of List.

Casting Interfaces

If you need access to a method that’s only declared in the concrete subclass, then you will need to cast the interface reference to that type, assuming the cast is supported at runtime.

The following is not permitted as the compiler detects they’re not related.

String l = "Bert";
Long t = (Long) l; // DOES NOT COMPILE

With interfaces, there’re limitations as to what the compiler can validate

interface Canine {}
class Dog implements Canine {}
class Wolf implements Canine {}

public class BadCasts {
	public static void main(String... args) {
		Canine canine = new Wolf();
		Canine badDog = (Dog) canine;
	}
}

This compiles, but it will throw a ClassCastException at runtime.

Interfaces and the instanceof Operator

The compiler has limited ability to report an error if two interfaces are related, because even though a reference type may not implement an interface, one of its subclasses could.

// COMPILES
Number tickets = 5;
if(tickets instanceof List) {}

A subclass that fits

public class MyNumber extends Number implements List {}

That said, the compiler can check for unrelated interfaces if this reference is a class that’s marked final.

Integer tickets = 6;
if(tickets instanceof List) {} // DOES NOT COMPILE

Reviewing Interface Rules

Interface Definition Rules

  1. Interfaces cannot be instantiated.
  2. All top-level types, including interfaces, cannot be marked protected or private.
  3. Interfaces are assumed to be abstract and cannot be marked final.
  4. Interfaces may include zero or more abstract methods.
  5. An interface can extend any number of interfaces.
  6. An interface reference may be cast to any reference that inherits the interface, although this may produce an exception at runtime if the classes aren’t related.
  7. The compiler will only report an unrelated type error for an instanceof operation with an interface on the right side if the reference on the left side is a final class that does not inherit the interface.
  8. An interface method with a body must be marked default, private, static or private static.

Abstract Interface Method Rules

  1. Abstract methods can be defined only in abstract classes or interfaces.
  2. Abstract methods cannot be declared private or final.
  3. Abstract methods must not provide a method body/implementation in the abstract class in which it’s declared.
  4. Implementing an abstract method in a subclass follows the same rules for overriding a method, including covariant return types, exception declaretions, etc.
  5. Interface methods without a body are assumed to be abstract and public.

The first 4 rules for abstract methods, whether they be defined in abstract classes or interfaces are exactly the same.

Interface Variable Rules

  1. Interface variables are assumed to be public, static, and final
  2. Because interface variables are marked final, they must be initialized with a value when they’re declared.

The primary difference between interfaces and abstract classes are that interfaces

  • Include implicit modifiers
  • Do not contain constructors
  • Do not participate in the instance initialization process
  • Support multiple inheritance

Introducing Inner Classes

Defining a Member Inner Class

A member inner class is a class defined at the member level of a class, or the same level as methods, instance variables, and constructors.
It is the opposite of a top-level class, in that it cannot be declared unless it’s inside another class.

This is useful is the relationship between the two classes is very close.

public class Zoo {
	private interface Paper {}
	public class Ticket implements Paper {}
}

While top-level classes and interfaces can only be set with public or package-private access, member inner classes don’t have the same restriction.
A member inner class can be declared with all access modifiers. Some members are disallowed in them though, such as static members.

public class Zoo {
	private interface Paper {
		public String getId();
	}

	public class Ticket implements Paper {
		private String serialNumber;
		public String getId() { return serialNumber; }
	}
}

Using a Member Inner Class

They can be used by calling it in the outer class.

public class Zoo {
	private interface Paper {
		public String getId();
	}

	public class Ticket implements Paper {
		private String serialNumber;
		public String getId() { return serialNumber; }
	}

	public Ticket sellTicket(String serialNumber) {
		var t = new Ticket();
		t.serialNumber = serialNumber;
		return t;
	}
}

The advantage here, is that Zoo completely manages the lifecycle of Ticket.

Summary

In this chapter, we presented advanced topics in class design, starting with abstract classes. An abstract class is just like a regular class except that it cannot be instantiated and may contain abstract methods. An abstract class can extend a nonabstract class, and vice versa. Abstract classes can be used to define a framework that other developers write subclasses against.

An abstract method is one that does not include a body when it is declared. An abstract method may be placed inside an abstract class or interface. Next, an abstract method can be overridden with another abstract declaration or a concrete implementation, provided the rules for overriding methods are followed. The first concrete class must implement all of the inherited abstract methods, whether they are inherited from an abstract class or interface.

An interface is a special type of abstract structure that primarily contains abstract methods and constant variables. Interfaces include implicit modifiers, which are modifiers that the compiler will automatically apply to the interface declaration. For the 1Z0-815 exam, you should know which modifiers are assumed in interfaces and be able to spot potential conflicts. When you prepare for the 1Z0-816 exam, you will study the four additional nonabstract methods that interfaces now support. Finally, while the compiler can often prevent casting to unrelated types, it has limited ability to prevent invalid casts when working with interfaces.

We concluded this chapter with a brief presentation of member inner classes. For the exam, you should be able to recognize member inner classes and know which access modifiers are allowed. Member inner classes, along with the other types of nested classes, will be covered in much more detail when you study for the 1Z0-816 exam.

Exam Essentials

Be able to write code that creates and extends abstract classes. In Java, classes and methodscan be declared as abstract. An abstract class cannot be instantiated. An instance of an abstract class can be obtained only through a concrete subclass. Abstract classes can include any number, including zero, of abstract and nonabstract methods. Abstract methods follow all the method override rules and may be defined only within abstract classes. The first concrete subclass of an abstract class must implement all the inherited methods. Abstract classes and methods may not be marked as final.

Be able to write code that creates, extends, and implements interfaces. Interfaces are specialized abstract types that focus on abstract methods and constant variables. An interface may extend any number of interfaces and, in doing so, inherits their abstract methods. An interface cannot extend a class, nor can a class extend an interface. A class may implement any number of interfaces.

Know the implicit modifiers that the compiler will automatically apply to an interface. All interfaces are assumed to be abstract. An interface method without a body is assumed to be public and abstract. An interface variable is assumed to be public, static, and final and initialized with a value when it is declared. Using a modifier that conflicts with one of these implicit modifiers will result in a compiler error.

Distinguish between top-level and inner classes/interfaces and know which access modifiers are allowed. A top-level class or interface is one that is not defined within another class declaration, while an inner class or interface is one defined within another class. Inner classes can be marked public, protected, package-private, or private.

Class Design

Understanding Inheritance

Inheritance is the process by which a subclass automatically includes any public or protected members of the class, including primitives, objects or methods defined in the parent class.

We refer to any class that inherits from another class a subclass or child class, as it’s considered a descendant of that class. Alternatively, we refer to the class that the child inherits from as the superclass or parent class, as it’s considered an ancestor of the class. Inheritance is transitive.

public class BigCat {
	public double size;
}

public class Jaguar extends BigCat {
	public Jaguar() {
		size = 10.2;
	}

	public void printDetails() {
		sout(size);
	}
}

size is accesible because it’s marked as public. Jaguar can read or write it as if it were its own member.

Single vs Multiple Inheritance

Java supports single inheritance, by which a class may inherit from only one direct parent class. It also supports multiple levels of inheritance, by which one class may extend another class, which in turn extends another class. You can have any number of levels of inheritance.

By design, Java doesn’t support multiple inheritance because it can lead to complex, often difficult-to-maintain data models. It allows though one exception - a class may implement multiple interfaces.

It is possible to prevent a class from being extended by marking it with final modifier. If you try to inherit from such a class, then the class will fail to compile.

Inheriting Object

All classes inherit from a single class: java.lang.Object. Object is the only class in Java that doesn’t have a parent class. You don’t need to extend Obect manually. The compiler automatically does the job for you, if you didn’t specify any extends to another class.

Primitives don’t inherit from Object, since they’re not classes.

Creating Classes

Extending a class

public abstract class ElephantSeal extends Seal {

}

Let’s check out an example

public class Animal {

	private int age;
	protected String name;

	public int getAge() {
		return age;
	}
	public void setAge(int newAge) {
		age = newAge;
	}

}

public class Lion extends Animal {
	public void setProperties(int age, String n) {
		setAge(age);
		name = n;
	}

	public void roar() {
		sout(name + ", age" + getAge() + ", says: Roar!");
	}
}

Lion may access to the getters, but be careful when accessing age directly, as it’s private and it wouldn’t compile.

public void roar() {
	sout("Lions age: " + age); // DOES NOT COMPILE
}

The name variable can be accessed directly, as it’s marked as protected.

Applying Class Access Modifiers

You can also apply access modifiers to class definitions. In Java, a top-level class is one that’s not defined inside another class. They can only have public or package-private access.

An inner class is a class defined inside of another class and is the opposite of a top-level class. Inner classes can also have protected and private access.

A Java file can have many top-level classes but at most one (or none) public top-level class. There’s also no requirement for the public class to be the first class in the file. One benefit of using the package-private access is that you can define many classes within the same Java file.

class Rodent {}

public class Groundhog extends Rodent {}

Accessing this reference

What do you think the following program prints?

public class Flamingo {
	private String color;

	public void setColor(String color) {
		color = color;
	}

	public static void main(String... unused) {
		Flamingo f = new Flamingo();
		f.setColor("PINK");
		sout(f.color);
	}
}

It prints null. When it sees color = color, it thinks you’re assigning the method parameter value to itself.

The fix is to use the this keyword. It refers to the current instance of the class and can be used to access any member of the class, including inherited members.
It cannot be used when there’s no implicit instance of the class, such as in a static method or static initializer block.

public void setColor(String color) {
	this.color = color;
}

Watch out for examples that aren’t common but you might see on the exam.

public void setColor(String color) {
	color = this.color; // BACKWARDS. NOT GOOD.  
}

If you see this example, be careful with data types and its default values.

Calling the super Reference

A variable or method can be defined in both a parent class and a child class. We reference the version in the parent or the current class with this and super. The super reference is similar to this reference, except that it excludes any members found in the current class. The member must instead be accessible via inheritance.

class Mammal {
	String type = "mammal";
}

public class Bat extends Mammal {
	String type = "bat";

	public String getType() {
		return super.type + ":" + this.type;
	}

	public static void main(String... zoo) {
		sout(new Bat().getType()); // mammal:bat
	}
}

Declaring Constructors

A constructor is a special method that matches the name of the class and has no return type. It’s called when a new instance of the class is created.

Creating a Constructor

public class Bunny {
	public bunny() { } // DOES NOT COMPILE
	public void Bunny() { } // VALID
}

The first one doesn’t match the class name, because it’s case sensitive, and it expects it to be a method, but it hasn’t a return type so it doesn’t compile.

The second one is perfectly valid but is a method, not a constructor as it has a return type.

(!) Like method parameters, constructor params can be any valid class, arrays… but may not include var. (!)

class Bonobo {
	public Bonobo(var food) { // DOES NOT COMPILE

	}
}

A class may have multiple constructors, so long as each constructor has a unique signature. This is refered to as constructor overloading.

public class Turtle {
	private String name;

	public Turtle() {
		name = "John Doe";
	}

	public Turtle(int age) {}
	public Turtle(long age) {}

	public Turtle(String age, String... favoriteFoods) {}
}

When calling the constructor with new keyword:

  1. Java allocates memory for the new object
  2. It looks for a constructor with a matching signature
  3. Calls it

Default constructor

Every class in Java has a constructor whether you code one or not. If you don’t include any constructors in the class, Java will create one for you without any parameters. This is called the default constructor and is added anytime a class is declared without any constructors.

public class Rabbit {
	public static void main(String... args) {
		Rabbit rabbit = new Rabbit(); // calls default constructor
	}
}

This happens during compile time.

public class Rabbit1 { } // DEFAULT CONSTRUCTOR

public class Rabbit2 {
	public Rabbit2() {} // NORMAL CONSTRUCTOR. IT'S NOT DEFAULT!
}

Having only private constructors in a class tells the compiler not to provide a default no-argument constructor. It also prevents other classes from instantiating the class.
This is useful when a class has only static methods or the developer wants to have full control of all calls to create new instances of the class.

(!) Remember, static methods in the class, including main(), may access private members and constructors. (!)

Calling Overloaded Constructors with this()

public class Hamster {
	private String color;
	private int weight;

	public Hamster(int weight) { // First constructor
		this.weight = weight;
		color = "brown";
	}

	public Hamster(int weight, String color) { // Second constructor
		this.weight = weight;
		this.color = color;
	}
}

For this example there’s a bit of duplication. To remove it, how can we have a constructor call another constructor?

public Hamster(int weight) {
	Hamster(weight, "brown"); // DOES NOT COMPILE
}

Constructors can be called only by writing new before the name of the constructor. They’re not like normal methdos that you can just call.

public Hamster(int weight) {
	new Hamster(weight, "brown"); // Compiles, but is incorrect
}

This would create a new object with default weight and color. It then constructs a different object with the desired weight and color.

public Hamster(int weight) {
	this(weight, "brown"); // correct solution
}

Instead, Java provides a solution this(). When it’s used with parentheses, it calls another constructor on the same instance of the class.

It has one rule though, if you choose to call it this() must be the first statement in the constructor. The side effect of this is that there can be only one call to this() in any constructor.

public Hamster(int weight) {
	sout("constructor"); // DOES NOT COMPILE
	this(weight, "brown");
}

There’s another rule, a constructor cannot call itself (or other constructors) in a loop.

public class Hamster {
	public Hamster(int food) {
		this(5); // DOES NOT COMPILE
	}
}
public class Hamster {
	public Hamster() {
		this(5); // DOES NOT COMPILE
	}

	public Hamster(int food) {
		this(); // DOES NOT COMPILE
	}
}

this vs this()

They’re very different. Be sure to know which is which.

  • this refers to an instance of the class.
  • this() refers to a constructor call within the class.

Calling Parent Constructors with super()

The first statement of every constructor is either a call to another constructor within the class, using this(), or a call to a constructor in the direct parent class, using super().

public class Animal {
	private int age;

	public Animal(int age) {
		super(); // refers to java.lang.Object
		this.age = age;
	}
}

public class Zebra extends Animal {
	public Zebra(int age) {
		super(age); // refers to animal
	}

	public Zebra() {
		this(4); // refers to Zebra with int arg
	}
}

Like calling this(), calling super() can only be used as the first statement of the constructor. The following example won’t compile.

public class Zoo {
	public Zoo() {
		sout("Zoo created");
		super(); // DOES NOT COMPILE
	}
}
public class Zoo {
	public Zoo() {
		super();
		sout("Zoo created");
		super(); // DOES NOT COMPILE
	}
}

If the parent class has more than one constructor, the child class may use any valid parent constructor in its definition.

super vs super()

super is used to reference members of the parent class, while super() calls a parent constructor.

Understanding Compiler Enhancements

The Java compiler automatically inserts a call to the no-argument constructor super() if you do not explicitly call this() or super().

Are Classes with only private Constructors Considered final?

A final class cannot be extended. If you have a class that’s not marked as final but only contains private constructors, you can extend the class, but only an inner class defined in the class itself can extend it.
An inner class is the only one that would have access to a private constructor and be able to all super().

Missing a Default No-Argument Constructor

What happens if the parent class doesn’t have a no-argument constructor? Remember it’s only inserted by the compiler only if there’s no constructor defined in the class.

public class Mammal {
	public Mammal(int age) {}
}

public class Elephant extends Mammal { // DOES NOT COMPILE
}

Since Elephant does not define any constructor, the Java compiler will attempt to insert a default no-argument constructor. It will also auto-insert a call to super(), but Mammal has at least one constructor, so the compiler does not insert a default no-args constructor. Therefore, it doesn’t compiles.

// the previous example is the same as:
public class Elephant extends Mammal {
	public Elephant() {
		super(); // DOES NOT COMPILE
	}
}

This is fixed by creating explicit constructor with an explicit call to the existing parent’s constructor.

public class Elephant extends Mammal {
	public Elephant() {
		super(10);
	}
}

(!) If an exam question has code regarding inheritance, check that the code compiles before answering a question about if. Be wary of any exam question in which a class defines a constructor that takes arguments and doesn’t define a no-argument constructor. (!)

super() always refers to the most direct parent

A class may have multiple ancestors via inheritance. For constructors, super() always refers to the most direct parent.

Constructors and final Fields

final static variables must be assigned a value exactly once. This may happen in the line of declaration and in a static initializer.

public class MouseHouse {
	private final int volume;
	private final String name = "The Mouse House";

	{
		volume = 10;
	}
}

They may also be initialized inside a constructor. By the time the constructor completes, all final instance variables must be assigned a value.

public class MouseHouse {
	private final int volume;
	private final String type;

	public MouseHouse() {
		this.volume = 10;
		type = "happy";
	}
}

Unlike local final variables, which are not required to have a value unless they’re actually used, final instance variables must be assigned a value. (!) Default values are not used for these variables. (!)
If they’re not assigned a value in the line where they’re declared or in an instance initializer, then they must be assigned a value in the constructor declaration. Failure to do so will result in a compiler error.

(!) On the exam, be wary of any instance variables marked final. Make sure they’re assigned a value in the line where they’re declared, in an instance initializer, or in a constructor. They should be assigned a value only once, and failure to assign a value is considered a compiler error in the constructor. (!)

Order of Initialization

Class Initialization

First you need to initialize the class, which involves invoking all static members in the class hierarchy, starting with the highest superclass and working downward. This is referred as loading the class.
The most important rule is that it happens at most once for each class.

Initialize Class X
  1. If there’s a superclass Y of X, the initialize class Y first.
  2. Process all static variable declarations in the order they appear in the class.
  3. Process all static initializers in the order they appear in the class.
public class Animal {
	static { sout("A"); }
}

public class Hippo extends Animal {
	static { sout("B"); }

	public static void main(String... grass) {
		sout("C");
		new Hippo();
		new Hippo();
		new Hippo();
	}
}

This will print ABC, since main() is inside Hippo class, the class will be initialized first, starting with its superclass.

A class must be initialized before it’s referenced or used. Also the class containing the program entry point is loaded before the main() method is executed.

Instance Initialization

An instance is initialized anytime the new keyword is used. Instance initialization is not the same as class initialization, because a class or superclass may have many constructors declared but only a handful used as part of instance initialization.

  1. First, start at the lowest level constructor where the new keyword is used.
    The first line of every constructor is a call this() or super() and if omitted, the compiler will automatically insert a call to the parent no-argument constructor super().
  2. Then, progress upward and note the order of constructors.
  3. Finally, initialize each class starting with the superclass, processing each instance initializer and constructor in the reverse order in which it was called.
Initialize Instance of X
Example 1
  1. If there’s a superclass Y of X, then initialize the instance of Y first.
  2. Process all instance variable declarations in the order they appear in the class.
  3. Process all instance initializers in the order they appear in the class.
  4. Initialize the constructor including any overloaded constructors referenced with this().
public class ZooTickets {
	private String name = "BestZoo";

	{ sout(name + "-"); }

	private static int COUNT = 0;

	static { sout(COUNT + "-"); }
	static { COUNT += 10; sout(COUNT+"-"); }

	public ZooTickets() {
		sout("z-");
	}

	public static void main(String... patrons) {
		new ZooTickets();
	}
}

This outputs 0-10-BestZoo-z-.

  1. First we have to initialize the class.
    Since there’s no superclass declared (which means it’s Object), we can start with the static components of ZooTickets.
  2. Next, we initialize the instance, so we start with the instance components.
  3. Finally, we run the constructor.
Example 2
class Primate {
	public Primate() {
		sout("Primate-");
	}
}

class Ape extends Primate {
	public Ape(int fur) {
		sout("Ape1-");
	}

	public Ape() {
		sout("Ape2-");
	}
}

public class Chimpanzee extends Ape {
	public Chimpanzee() {
		super(2);
		sout("Chimpanzee-");
	}

	public static void main(String... args) {
		new Chimpanzee();
	}
}

This outputs Primate-Ape1-Chimpanzee-.

Example 3
public class Cuttlefish {
	private String name = "swimmy";
	{ sout(name) }
	private static int COUNT = 0;
	static { sout(COUNT); }
	{ COUNT++; sout(COUNT); }

	public Cuttlefish() {
		sout("Constructor");
	}

	public static void main(String... args) {
		sout("Ready");
		new Cuttlefish();
	}
}

This outputs the following (wtf?)

0
Ready
swimmy
1
Constructor
  1. There’s no superclass declared, so we can skip any steps that relate to inheritance.
  2. We first process static variables, and static initializers.
  3. Then the main() method can run.
  4. Then goes instance initializers.
  5. Finally, the constructor.
Example 4

(This example is a bit too harsh, take a look on page 378 of the book to review it again)

class GiraffeFamily {
	static { sout("A"); }
	{ sout("B"); }

	public GiraffeFamily(String name) {
		this(1);
		sout("C");
	}

	public GiraffeFamily() {
		sout("D");
	}

	public GiraffeFamily(int stripes) {
		sout("E");
	}
}

public class Okapi extends GiraffeFamily {
	static { sout("F"); }

	public Okapi(int stripes) {
		super("sugar");
		sout("G");
	}

	{ sout("H"); }

	public static void main(String[] grass) {
		new Okapi(1);
		soutln();
		new Okapi(2);
	}
}

This outputs

AFBECHG
BECHG
  1. Start with initializing Okapi class. Since it has a superclass, initialize it first printing A.
  2. Next initialize Okapi, printing F.
  3. After the classes are initialized, execute main(). The first line creates a new Okapi object, triggering the instance initialization process. Per the first rule, the superclass instance is initialized first, and within it the instance initializer is called and B printed.
  4. Initialize the constructors, which calls the overloaded constructor. This prints EC.
  5. Initialization of the Okapi instance itself. This prints HG.
  6. Line break
  7. Initialization of a new Okapi object. The order is the same as the previous one, sans the class initialization, so BECHG is printed again.

Reviewing Constructor Rules

  1. The first statement of every constructor is a call to an overloaded construcctor via this(), or a direct parent constructor via super().
  2. If the first statement of a constructor is not either this() or super(), then the compiler will insert a no-argument super() as the first statement.
  3. Calling this() and super() after the first statement of a constructor results in a compiler error.
  4. If the parent class doesn’t have a no-argument constructor, then every constructor in the child class must start with an explicit this() or super()
  5. If the parent class doesn’t have a no-argument constructor and the child doesn’t define any constructor, then the child class will not compile.
  6. If a class only defines private constructors, then it cannot be extended by a top-level class.
  7. All final instance variables must be assigned a value exactly once by the end of the constructor. Any final instance variables not assigned a value will be reported as a compiler error on the line the constructor is declared.

Make sure you understand these rules. The exam will often provide code that breaks one or many of these rules and therefore, doesn’t compile.

Inheriting Members

Calling Inherited Members

Java classes may use any public or protected member of the parent class, including methods, primitives or object references. If the parent class and child class are part of the same package, then it may also use any package-private members defined in the parent class. Finally, a child class may never access a private member of the parent class.

Inheriting Methods

Inheriting a class also sets the stage for collisions between methods defined in both the parent and the subclass.

Overriding a Method

If there’s a method defined in both the parent and child classes, with the same signature and you want to define a new version of the method and have it behave differently for that subclass, the solution is to override the method in the child class.
Overriding a method occurs when a subclass declares a new implementation for an inherited method with the same signature (method’s name and parameters) and compatible return type.

When you override a method, you may reference the parent version using super. In this manner this and super allow you to select between the current and parent versions.

public class Canine {
	public double getAverageWeight() {
		return 50;
	}
}

public class Worf extends Canine {
	public double getAverageWeight() {
		return super.getAverageWeight() + 20;
	}

	public static void main(String... args) {
		sout(new Canine().getAverageWeight()); // 50.0
		sout(new Worlf().getAverageWeight()); // 70.0
	}
}

If we didn’t use super. in the previous example, the method would call it itself recursively in a closed loop.

To override a method, the compiler performs the following checks

  1. The method in the child class, must have the same signature as the parents’
  2. The method in the child class, must be at least as accessible as the method in the parent class.
  3. The method in the child class may not declare a checked exception that’s new or broader than the class of any exception declared in the parent.
  4. If the method returns a value, it must be the same or a subtype of the method in the parent class, known as covariant return types.
Overloading vs Overriding

An overloaded method will use a different list of method parameters. This allows overloaded methods a great deal more freedom in syntax than an overriden method would have.

First Rule - modify return type
public class Bird {
	public void fly() {
		sout("Bird is flying");
	}

	public void eat(int food) {
		sout("Bird is eating + food + " units of food);
	}
}

public class Eagle extends Bird {
	public int fly(int height) {
		sout("Bird is flying at " + height + " meters");
		return height;
	}

	public int eat(int food) { // DOES NOT COMPILE
		sout("Bird is eating " + food + " units of food");
		return food;
	}
}

The fly() method is overloaded in Eagle. Because it’s being overloaded and not overridden, the return type can be changed from void to int.

The eat() method is overridden in Eagle, since the signature is the same as in its parent class. The new return type must be compatible with the return type from the parent.

(!) Any time you see a method on the exam with the same name as a method in the parent class, determine whether the method is being overloaded or overriden first; doing so will help you with questions about whether the code will compile. (!)

Second Rule - access modifier
public class Camel {
	public int getNumberOfHumps() {
		return 1;
	}
}

public class BactrianCamel extends Camel {
	private int getNumberOfHumps() { // DOES NOT COMPILE
		return 2;
	}
}

This fails because it tries to override the method, but fails because the access modifier private is more restrictive than the one defined in the parent version.

Third Rule - Checked exceptions

Overriding a method cannot declare new checked exceptions or checked exceptions broader than the inherited method. It may declare though, a checked exception more restricted than the inherited version.

public class Reptile {
	protected void sleep() throws IOException {}

	protected void exit() throws FileNotFoundException {}
}

public class GalapagosTortoise extends Reptile {
	public void sleep() throws FileNotFouncException {} // COMPILES

	public void exit() throws IOException {} // DOES NOT COMPILE
}
Fourth Rule - Covariant return type

This is the most complicated, as it requires knowing the relationships between the return types. The overriding method must use a return type that is covariant with the return type of the inherited method.

public class Rhino {
	protected CharSequence getName() {
		return "rhino";
	}

	protected String getColor() {
		return "grey, black, or white";
	}
}

class JavanRhino extends Rhino {
	public String getName() {
		return "javan rhino";
	}

	public CharSequence getColor() { // DOES NOT COMPILE
		return "grey";
	}
}

You should know that String implemenents CharSequence interface, making it a subtype. All String values are CharSequence values, but not viceversa.

(!) How to check if they’re covariant?: Given an inherited return type A and an overriding return type B, can you assign an instance of B to a reference variable for A without a cast? If so, then they’re covariant. (!)

Overriding a Generic Method

Review of Overloading a Generic Method

You cannot overload methods by changing the generyc type, due to type erasure.

public class LongTailAnimal {
	protected void chew(List<Object>) input {}
	protected void chew(List<Double>) input {} // DOES NOT COMPILE
}

For the same reason, you also can’t overload a generic method in a parent class.

public class LongTailAnimal {
	protected void chew(List<Object> input) {}
}

public class Anteater extends LongTailAnimal {
	protected void chew(List<Double> input) {} // DOES NOT COMPILE
}

Both of them fail to compile because of type erasure. When compiled, the generic type is dropped.

Generic Method Parameters

You can override though a method with generic params, but you must match the signature exactly.

public class LongTailAnimal {
	protected void chew(List<String> input) {}
}

public class Anteater extends LongTailAnimal {
	protected void chew(List<String> input) {}
}

The generic class or interface may change, but then it’s considered overloading, not overriding as the method signature is not the same.

public class LongTailAnimal {
	protected void chew(List<String> input) {}
}

public class Anteater extends LongTailAnimal {
	protected void chew(ArrayList<String> input) {} // COMPILES
}
Generics and Wilcards

Java includes support for generic wildcards using the question mark ? character.

void sing1(List<?> v) {} // unbounded wildcard
void sing2(List<? super String> v) {} // lower bounded wildcard
void sing3(List<? extends String> v) {} // upper bounded wildcard
Generic Return Types

When you’re working with overriden methods that return generics, the return values must be covariant. In terms of generics, this means that the return type of the class or interface declared in the overriding method must be a subtype of the class defined in the parent class. The generic param type must match its parent’s type exactly.

public class Mammal {
	public List<CharSequence> play() {}
	public CharSequence sleep() {}
}

public class Monkey extends Mammal {
	public ArrayList<CharSequence> play() {}
}

public class Goat extends Mammal {
	public List<String> play() {} // DOES NOT COMPILE
	public String sleep() {}
}

Monkey compiles because ArrayList is a subtype of List.
The play() method in the Goat class doesn’t compile, because the generic type param must match. Even though String is a subtype of CharSequence, it doesn’t exactly match the generic type defined in Mammal.

Redeclaring private Methods

In Java, you can’t override private methods since they’re not inherited. Just because a child class doesn’t have access to the parent method doesn’t mean the child class can’t define its own version of the method. It just means, strictly speaking, that the new method is not an overriden version of the parent class’ method.

public class Camel {
	private String getNumberOfHumps() {
		return "Undefined";
	}
}

public class DromedaryCamel extends Camel {
	private int getNumberOfHumps() {
		return 1;
	}
}

The methods are unrelated. If the paren’t method were public or protected, the method in the child class wouldn’t compile.

Hiding Static Methods

A hidden method occurs when a child class defines a static method with the same name and signature as an inherited static method defined in a parent class.

It’s not exactly the same as method overriding. The same previous four rules apply here. In addition, a new rule is added:

The method defined in the child class must be marked as static if it’s marked as static in a parent class. If one is marked static and the other is not, the class will not compile.

public class Bear {
	public static void eat() {
		sout("Bear is eating");
	}
}

public class Panda extends Bear {
	public static void eat() {
		sout("Panda is chewing");
	}
}

This compiles and runs. The eat() method in Panda class hides the eat() method in Bear. Because they’re both marked static, this is not considered an overridden method.

public class Bear {
	public static void sneeze() {
		sout("Bear is sneezing");
	}

	public void hibernate() {
		sout("Bear is hibernating");
	}

	public static void laugh() {
		sout("Bear is laughing");
	}
}

public class Panda extends Bear {
	public void sneeze() { // DOES NOT COMPILE

	}

	public static void hibernate() { // DOES NOT COMPILE

	}

	protected static void laugh() { // DOES NOT COMPILE

	}
}
Creating final Methods

final methods cannot be replaced. By marking a method as final, you forbid a child class from replacing this method. This rule applies both, when you try to override a method and when you hide a method.

public class Bird {
	public final boolean hasFeathers() {
		return true;
	}

	public final static void flyAway() {}
}

public class Penguin extends Bird {
	public final boolean hasFeathers() { // DOES NOT COMPILE
		return false;
	}

	public final static void flyAway() {} // DOES NOT COMPILE
}
Hiding Variables

Java doesn’t allow variables to be overriden. They can be hidden, though.
A hidden variable occurs when a child class defines a variable with the same name as an inherited variable defined in the parent class.

class Carnivore {
	protected boolean hasFur = false;  
}

public class Meerkat extends Carnivore {
	protected boolean hasFur = true;

	public static void main(String... args) {
		Meerkat m = new Meerkat() ;
		Carnivore c = m;
		sout(m.hasFur()); // true
		sout(c.hasFur()); // false
	}
}

Understanding Polymorphism

Polymorphism is the property of an object to take on many different forms. A Java object may be accessed using a reference with the same type as the object, a reference that’s a superclass of the object, or a reference that defines an interface the object implements, either directly or through a superclass.

Interface Primer

  • An interface can define abstract methods.
  • A class can implement any number of interfaces.
  • A class implements an interface by overriding the inherited abstract methods.
  • An object that implements an interface can be assigned to a reference for that interface.
public class Primate {
	public boolean hasHair() {
		return true;
	}
}

public interface HasTail {
	public abstract boolean isTailStriped();
}

public class Lemur extends Primate implements HasTail {
	public int age = 10;

	public boolean isTailStriped() {
		return false;
	}

	public static void main(String[] args) {
		Lemur lemur = new Lemur();
		sout(lemur.age);

		HasTail hasTail = lemur;
		sout(hasTail.isTailStriped());

		Primate primate = lemur;
		sout(primate.hasHair());
	}
}

This compiles and outputs

10
false
true

There’s only one object (lemur) created and referenced. Polymorphism enables an instance of Lemur to be reassigned or passed to a method using one of its supertypes, such as Primate or HasTail.

Once the object has been assigned to a new reference type, only the methods and variables available to the reference type are callable on the object without an explicit cast.

HasTail hasTail = lemur;
sout(hasTail.age); // DOES NOT COMPILE  

Primate primate = lemur;
sout(primate.isTailStriped()); // DOES NOT COMPILE  

In this example, hasTail has direct access only to methods defined in HasTail interface.

Object vs Reference

In Java, all objects are accessed by reference. You should consider the object as the entity that exists in memory, allocated by the Java runtime environment. Regardless of the type of the reference you have for the object in memory, the object itself doesn’t change.

Since all objects inherit from Object, they can all be reassigned to it.

Lemur lemur = new Lemur();
Object lemurAsObject = lemur;

The object itself doesn’t change and still exists as a Lemur object in memory. What has changed though, is our ability to access methods within the Lemur class with the lemurAsObject reference. Without an explicit cast back to Lemur we no longer have access to the Lemur properties of the object.

  1. The type of the object determines which properties exist within the object in memory.
  2. The type of the reference to the object determines which methods and variables are accessible to the Java program.

Casting Objects

Once we change the reference type, we loose access to more specific members defined in the subclass that still exist within the object. We can reclaim those references by casting the object back to the specific subclass it came from.

Primate primate = new Lemur(); // implicit cast

Lemur lemur2 = primate; // DOES NOT COMPILE

Lemur lemur3 = (Lemur) primate;
sout(lemur3.age);

If the underlying object is not compatible with the type, then a ClassCastException will be thrown at runtime.

  1. Casting a reference from subtype to a supertype doesn’t require an explicit cast.
  2. Casting a reference from a supertype to a subtype requires an explicit cast
  3. The compiler disallows cast to an unrelated class.
  4. At runtime, an invalid cast of a reference to an unrelated type results in ClassCastException

(!) The exam may trick you with a cast that the compiler doesn’t allow. For example (!)

public class Bird {}

public class Fish {
	public static void main(String[] args) {
		Fish fish = new Fish();
		Bird bird = (Bird) fish; // DOES NOT COMPILE
	}
}

Casting is not without limitations.

public class Rodent {}

public class Capybara extends Rodent {
	public static void main(String[] args) {
		Rodent rodent = new Rodent();
		Capybara capybara = (Capybara) rodent; // ClassCastException
	}
}

Keep in mind the Rodent object created does not inherit the Capybara class in any way.

(!) When reviewing a question that involves casting and polymorphism, be sure to remember what the instance of the object actually is. Then, focus on whether the compiler will allow the object to be referenced with or without explicit casts. (!)

The instanceof Operator

It can be used to check whether an object belongs to a particular class or interface and to prevent ClassCastExceptions at runtime.

if(rodent instanceof Capybara) {
	Capybara capybara = (Capybara) rodent;
}

Just as the compiler doesn’t allow casting an object to unrelated types, it also doesn’t allow instanceof to be used with unrelated types.

Fish fish = new Fish();
if(fish instanceof Bird) { // DOES NOT COMPILE

}

Polymorphism and Method Overriding

Polymorphism states that when you override a method, you replace all calls to it, even those defind in the parent class.

class Penguin {
	public int getHeight() { return 3; }

	public void printInfo() {
		sout(this.getHeight());
	}
}

public class EmperorPenguin extends Penguin {
	public int getHeight() { return 8; }

	public static void main(String[] fish) {
		new EmperorPenguin().printInfo(); // prints 8
	}
}

The getHeight() method is overridden in the subclass, meaning all calls to it are replaced at runtime. Despite printInfo() being defined in the Penguin class, calling getHeight() on the object calls the method associated with the precise object in memory, not the current reference type where it’s called.

Remember, you can choose to limit polymorphic behaviour by marking methods final, which prevents them from being overriden by a subclass.

class Penguin {
	public int getHeight() { return 3; }

	public void printInfo() {
		sout(this.getHeight());
	}
}

public class EmperorPenguin extends Penguin {
	public int getHeight() { return 8; }

	public void printInfo() {
		sout(super.getHeight());
	}

	public static void main(String[] fish) {
		new EmperorPenguin().printInfo(); // prints 3!
	}
}

Overriding vs Hiding Members

Overriding replaces the method everywhere it’s called. static method and variable hiding does not. Hiding members is not a form of polymorphism since the methods and variables maintain their individual properties.

Hiding members is very sensitive to the reference type and location where the member is being used.

class Penguin {
	public static int getHeight() { return 3; }

	public void printInfo() {
		sout(this.getHeight());
	}
}

public class CrestedPenguin extends Penguin {
	public static int getHeight() { return 8; }

	public static void main(String[] fish) {
		new CrestedPenguin().printInfo(); // prints 3
	}
}

The printInfo() method is hidden, not overriden. Calling getHeight() in CrestedPenguin returns a different value than calling it in Penguin, even if the underlying object is the same.

This constrasts with overriding a method, where it returns the same value for an object regardless of which class it’s called in.

(!) It’s permitted to use an instance reference to access a static variable or method. It’s discouraged though, but it works. (!)

class Marsupial {
	protected int age = 2;

	public static boolean isBiped() {
		return false;
	}
}

public class Kangaroo extends Marsupial {
	protected int age = 6;

	public static boolean isBiped() {
		return true;
	}

	public static void main(String[] args) {
		Kangaroo k = new Kangaroo();
		Marsupial m = k;
		sout(k.isBiped());
		sout(m.isBiped());
		sout(k.age);
		sout(m.age);
	}
}

This outputs

true
false
6
2

In this example, only one object, of type Kangaroo is created and stored in memory. Since static methods can only be hidden and not overriden, Java uses the reference type to determine which version of isBiped() should be called, resulting in k.isBiped() printing true and m.isBiped() printing false.
Likewise, the age variable is hidden, not overriden, so the reference type is used to determine which value to output.

Summary

This chapter took the basic class structures we’ve presented throughout the book and expanded them by introducing the notion of inheritance. Java classes follow a multilevel single-inheritance pattern in which every class has exactly one direct parent class, with all classes eventually inheriting from java.lang.Object.

Inheriting a class gives you access to all of the public and protected members of the class. It also gives you access to package-private members of the class if the classes are in the same package. All instance methods, constructors, and instance initializers have access to two special reference variables: this and super. Both this and super provide access to all inherited members, with only this providing access to all members in the current class declaration.

Constructors are special methods that use the class name and do not have a return type. They are used to instantiate new objects. Declaring constructors requires following a number of important rules. If no constructor is provided, the compiler will automatically insert a default no-argument constructor in the class. The first line of every constructor is a call to an overloaded constructor, this(), or a parent constructor, super(); otherwise, the compiler will insert a call to super() as the first line of the constructor. In some cases, such as if the parent class does not define a no-argument constructor, this can lead to compilation errors. Pay close attention on the exam to any class that defines a constructor with arguments and doesn’t define a no-argument constructor.

Classes are initialized in a predetermined order: superclass initialization; static variables and static initializers in the order that they appear; instance variables and instance initializers in the order they appear; and finally, the constructor. All final instance variables must be assigned a value exactly once. If by the time a constructor finishes, a final instance variable is not assigned a value, then the constructor will not compile.

We reviewed overloaded, overridden, hidden, and redeclared methods and showed how they differ, especially in terms of polymorphism. A method is overloaded if it has the same name but a different signature as another accessible method. A method is overridden if it has the same signature as an inherited method, with access modifiers, exceptions, and a return type that are compatible. A static method is hidden if it has the same signature as an inherited static method. Finally, a method is redeclared if it has the same name and possibly the same signature as an uninherited method.

We also introduced the notion of hiding variables, although we strongly discourage this in practice as it often leads to confusing, difficult-to-maintain code.

Finally, this chapter introduced the concept of polymorphism, central to the Java language, and showed how objects can be accessed in a variety of forms. Make sure you understand when casts are needed for accessing objects, and be able to spot the difference between compile-time and runtime cast problems.

Exam Essentials

Be able to write code that extends other classes. A Java class that extends another class inherits all of its public and protected methods and variables. If the class is in the same package, it also inherits all package-private members of the class. Classes that are marked final cannot be extended. Finally, all classes in Java extend java.lang.Object either directly or from a superclass.

Be able to distinguish and make use of this, this(), super, and super(). To access a current or inherited member of a class, the this reference can be used. To access an inherited member, the super reference can be used. The super reference is often used to reduce ambiguity, such as when a class reuses the name of an inherited method or variable. The calls to this() and super() are used to access constructors in the same class and parent class, respectively.

Evaluate code involving constructors. The first line of every constructor is a call to another constructor within the class using this() or a call to a constructor of the parent class using the super() call. The compiler will insert a call to super() if no constructor call is declared. If the parent class doesn’t contain a no-argument constructor, an explicit call to the parent constructor must be provided. Be able to recognize when the default constructor isprovided. Remember that the order of initialization is to initialize all classes in the class hierarchy, starting with the superclass. Then, the instances are initialized, again starting with the superclass. All final variables must be assigned a value exactly once by the time the constructor is finished.

Understand the rules for method overriding. Java allows methods to be overridden, or replaced, by a subclass if certain rules are followed: a method must have the same signature, be at least as accessible as the parent method, must not declare any new or broader exceptions, and must use covariant return types. The generic parameter types must exactly match in any of the generic method arguments or a generic return type. Methods marked final may not be overridden or hidden.

Understand the rules for hiding methods and variables. When a static method is overridden in a subclass, it is referred to as method hiding. Likewise, variable hiding is when an inherited variable name is reused in a subclass. In both situations, the original method or variable still exists and is accessible depending on where it is accessed and the reference type used. For method hiding, the use of static in the method declaration must be the same between the parent and child class. Finally, variable and method hiding should generally be avoided since it leads to confusing and difficult-to-follow code.

Recognize the difference between method overriding and method overloading. Both method overloading and overriding involve creating a new method with the same name as an existing method. When the method signature is the same, it is referred to as method overriding and must follow a specific set of override rules to compile. When the method signature is different, with the method taking different inputs, it is referred to as method overloading, and none of the override rules are required. Method overriding is important to polymorphism because it replaces all calls to the method, even those made in a superclass.

Understand polymorphism. An object may take on a variety of forms, referred to as polymorphism. The object is viewed as existing in memory in one concrete form but is accessible in many forms through reference variables. Changing the reference type of an object may grant access to new members, but the members always exist in memory.

Recognize valid reference casting. An instance can be automatically cast to a superclass or interface reference without an explicit cast. Alternatively, an explicit cast is required if the reference is being narrowed to a subclass of the object. The Java compiler doesn’t permit casting to unrelated class types. Be able to discern between compiler-time casting errors and those that will not occur until runtime and that throw a ClassCastException.

Methods and Encapsulation

Designing methods

Two of the methods parts - method name and parameter list - are called the method signature.

Access Modifiers

  • private. It means the method can be called only from within the same class.
  • default package-private. The method can be called only from classes in the same package.
  • protected. It means the method can be called only from classes in the same package or subclasses.
  • public. Means the method can be called from any class.

(!) The exam creators like to trick you by putting method elements in the wrong order or using incorrect values (!)

public void walk1() {}
default void walk2() {} // DOES NOT COMPILE
void public walk3() {} // DOES NOT COMPILE
void walk4() {}

Optional Specifiers

  • static. It’s used for class methods
  • abstract. It’s used when a method body is not provided
  • final It’s used when a method is not allowed to be overriden by a subclass
  • synchronized It’s used with multithreaded code
  • native it’s used when interacting with code written in another language, such as C++
  • strictfp. It’s used for making float-point calculations portable
public void walk() {}
public final void walk() {}
public static final void walk() {}
public final static void walk() {}
public modifier void walk5() {} // DOES NOT COMPILE
public void final walk() {} // DOES NOT COMPILE
final public void walk() {}

Return Type

Methods with a return type other than void are required to have a return statement inside the method body.

public void walk() {}
public void walk() { return; }
public String walk() { return ""; }
public String walk() {} // DOES NOT COMPILE
public walk() {} // DOES NOT COMPILE
public String int walk() {} // DOES NOT COMPILE
String walk(int a) { if(a==4) return ""; } // DOES NOT COMPILE
int longMethod() {
	return 9L; // DOES NOT COMPILE
}

Method Name

They follow the same rules as variable names.

  • They mey only contain letters, numbers, $ or _
  • The first character is not allowed to be a number.
  • Reserved words are not allowed.
  • A single underscore char is not allowed.
public void walk1() {}
public void 1walk() {} // DOES NOT COMPILE
public walk1 void() {} // DOES NOT COMPILE
public void Walk_$() {}
public _() {} // DOES NOT COMPILE
public void() {} // DOES NOT COMPILE

Parameter List

Altough the list is required, it doesn’t have to contain any parameters. If you have multiple parameters, you separate them with a comma.

public void walk() {}
public void walk {} // DOES NOT COMPILE
public void walk(int a) {}
public void walk(int a; int b) {} // DOES NOT COMPILE
public void walk(int a, int b) {}

Optional Exception List

You can list as many types of exceptions as you want, separated by commas

public void zeroExceptions() {}
public void oneException() throws IllegalArgumentException {}
public void twoExceptions() throws IllegalArgumentException, InterruptedException {}

Method Body

It’s simply a code of block. It’s required.

Working with Varargs

A varargs parameter must be the last element in a method’s parameter list.

public void walk(int)... nums {}
public void walk(int start, int... nums) {}
public void walk(int... nums, int start) {} // DOES NOT COMPILE
public void walk(int... start, int... nums) {} // DOES NOT COMPILE

When calling a method with vararg params, you can either pass in an array, or list the elements of the array and let Java create it for you. You can even omit the varargs values in the method call and it will create an array of 0 length.

public static void walk(int start, int... nums) {
	sout(nums.length());
}

public static void main(String... args) {
	walk(1); // 0
	walk(1, 2); // 1
	walk(1, 2, 3); // 2
	walk(1, new int[] {2, 3}); // 2

	walk(1, null); // NullPointerException
}

Applying Access Modifiers

Private Access

Only code in the same class can call private methods or access private fields.

Default (Package-Private) Access

When there’s no access modifier, Java uses the default, which is package-private. This means that the member is “private” to classes in the same package. In other words, only classes in the package may access it.

Protected Access

It allows everything that default access allows and more. It adds the ability to access members of a parent class.
The definition of protected allows access to subclasses and classes in the same package.

We have the following class with protected fields and methods

package pond.shore;

public class Bird {
	protected String text = "floating";

	protected void floatInWater() {
		sout(text);
	}
}

Now we create a subclass

package pond.goose;

import pond.shore.Bird; // different package

public class Gosling extends Bird {

	public void swim() {
		floatInWater(); // calling protected member
		sout(text); // accessing protected member
	}

}

Class in same package, but not a subclass

package pond.shore; // same package as Bird

public class BirdWatcher {

	public void watchBird() {
		Bird bird = new Bird();
		bird.floatInWater(); // calling protected member
		sout(bird.text); // accessing protected member
	}

}

Class in different package, not a subclass

package pond.inland;

import pond.shore.Bird; // different package

public class BirdWatcherFromAfar {

	public void watchBird() {
		Bird bird = new Bird();
		bird.floatInWater(); // DOES NOT COMPILE
		sout(bird.text); // DOES NOT COMPILE
	}

}

Subclasses and classes in the same package are the only ones allowed to access protected members.

Check the following case

package pond.swan;

import pond.shore.Bird; // different package than Bird

public class Swan extends Bird { // subclass of Bird

	public void swim() {
		floatInWater(); // subclass access to superclass
	}

	public void helpOtherSwanSwim() {
		Swan other = new Swan();
		other.floatInWater(); // subclass access to superclass (is same class)
	}

	public void helpOtherBirdSwim() {
		Bird other = new Bird();
		other.floatInWater(); // DOES NOT COMPILE
	}

}

Altough Bird is the superclass, it’s referenced as a variable rather than inherited. This is why it won’t work.

The protected rules apply under two scenarios:

  • A member is used without referring to a variable. We are taking advantage of inheritance and protected access is allowed.
  • A member is used through a variable. If it’s a subclass, protected access is allowed.

Public Access

public means anyone can access the member from anywhere.

Applying the static keyword

The static keyword applies to the class, rather than a specific instance of the class.

Designing static Methods and Fields

static methods don’t require an instance of the class. They’re shared among all users of the class. It exists independently of any instances of that class.

public class KoalaTester {
	public static void main(String... args) {
		Koala.main(new String[0]);
	}
}

They have two main purposes

  • For utility or helper methods that don’t require any object state, since there’s no need to access instance variables.
  • For state that’s shared by all instances of a class, like a counter.

Accessing a static Variable or Method

You can use an instance of the object to call a static method.
This is legal.

spit(Koala.count); // 0

Koala k = new Koala();
sout(k.count); // 0

k = null;
sout(k.count); //0! This still works

For the last example, Java doesn’t care that k happens to be null, since we are looking for a static it doesn’t matter.

Static vs Instance

A static member cannot call an instance member without referencing an instance of the class.

public class Static {
	private String name = "Static class";

	public static void first() {}
	public static void second() {}
	public void third() {sout(name)}
	public static void main(String... args) {
		first();
		second();
		third(); // DOES NOT COMPILE
		// new Static().third(); // would compile
	}
}

Another example

1: 	public class Gorilla {
2: 		public static int count;
3: 		public static void addGorilla() { count++; }
4: 		
5: 		public void babyGorilla() { count++; }
6: 		
7:	 	public void announceBabies() {
8: 			addGorilla();
9: 			babyGorilla();
10: 	}
11: 	
12: 	public static void announceBabiesToEveryone() {
13: 		addGorilla();
14: 		babyGorilla(); // DOES NOT COMPILE
15: 	}
16: 	
17: 	public int total;
18: 	public static double average = total / count; // DOES NOT COMPILE
19: }

Lines 7-10 are fine because both static and instance methods can refer to a static variable.

Lines 12-15 doesn’t compile because a static method cannot call an instance method.

Line 18 doesn’t compile because a static variable is trying to use an instance variable.

static Variables

Some static variables are meant to change as the programs runs, such as counters.

public class Initializers {
	private static int counter = 0;
}

Others are meant to never change during the program. This type of variable is known as a constant.

public class Initializers {
	private static final int NUM_BUCKETS = 435;

	public static void main(String... args) {
		NUM_BUCKETS = 5; // DOES NOT COMPILE
	}
}

Static Initialization

static initialization specifies they should be run when the class is first loaded.

private static final int NUM_SECONDS_PER_MINUTE;

static {
	NUM_SECONDS_PER_MINUTE = 60;
}

They’re run when the class is first used in the order they’re defined. The statements in them run and assign any static variables as needed.

final variables aren’t allowed to be reassigned.

private static int one;
private static final int two;
private static final int three = 3;
private static final int four; // DOES NOT COMPILE

static {
	one = 1;
	two = 2;
	three = 3; // DOES NOT COMPILE
	two = 20; // DOES NOT COMPILE
}
  • one compiles because it’s not final, so it can be re-assigned as many times as we want
  • two is final but not initialized until later. as final it can only be assigned once.
  • three was already assigned when created.
  • four never gets initialized and as final, static blocks are the only place where it could be initialized.

(!) They’re good for when you need to initialize a static field and the code required to do so requires more than one line. This occurs when you want to initialize a collection like an ArrayList.

Static Imports

import java.utils.List;
import static java.util.Arrays.asList; // static import

public class StaticImports {
	public static void main(String... args) {
		List<String> list = asList("one", "two"); // no Arrays.
	}
}

Regular imports are for importing classes. Static imports are for importing static member of classes.
The idea is that you shouldn’t have to specify where each static method or variable comes from each time you use it.

If we created an asList method in our class, Java would give it preference over the imported one, and our method would be used.

1: import static java.util.Arrays; // DOES NOT COMPILE
2: import static java.util.Arrays.asList;
3: static import java.util.Arrays.*; // DOES NOT COMPILE

4: public class BadStaticImports {
5: 	public static void main(String... args) {
6: 		Arrays.asList("one"); // DOES NOT COMPILE
7: 	}
8: }

Line 1 fails because static imports are only for importing static members.
Line 6 fails, because the asList method is imported on line 2, however the Arrays class is not imported anywhere. This makes it okay to write asList("one"), but not Arrays.asList("one").

You cannot do an static import of two methods with the same name or two static variables with the same name.

import static statics.A.TYPE;
import static statics.B.TYPE; // DOES NOT COMPILE

Passing Data among Methods

Java is pass-by-value. A copy of the variable is made and the method receives that copy. Assignments made in the method do not affect the caller.

This is an example for variable assignments.

public static void main(String... args) {
	String name = "Webby";
	speak(name);
	sout(name); // Webby
}

public static void speak(String name) {
	name = "Sparky";
}

We can also call methods on the parametes passed into methods.

public static void main(String... args) {
	StringBuilder name = new StringBuilder();
	speak(name);
	sout(name); // Webby
}

public static void speak(StringBuilder s) {
	s.append("Webby");
}

In the last case, the output is Webby because the method calls a method on the param. It doesn’t reassign name to a different object.

Getting data back from a method is easier. A copy is made of the primitive or reference and returned from the method. Most of the time, this returned value is used or stored in a variable. If the return value is not used, the result is ignored.

public class ReturningValues {
	public static void main(String... args) {
		int number = 1; // number = 1
		String letters = "abc"; // letters = abc
		number(number); // still, number = 1
		letters = letters(letters); // letters = abcd
	}

	public static int number(int number) {
		number++;
		return number;
	}

	public static String letters(String letters) {
		letters += "d";
		return letters;
	}
}

Overloading Methods

Method overloading occurs when methods have the same name but different method signatures, which means they differ by method parameters. Aside from this, they may have different access modifiers, specifiers (static), return types and exception lists.

These are all valid overloaded methods.

public void fly(int numMiles) {}
public void fly(short numFeet) {}
public boolean fly() { return false; }
void fly(int numMiles, short numFeet) {}
public void fly(short numFeet, int numMiles) throws Exception {}

We can overload by changing anything in the parameter list.

public void fly(int numMiles) {}
public fly(int numMiles) {} // DOES NOT COMPILE

This method doesn’t compile because it differs from the original only by return type.

public void fly(int numMiles) {}
public static void fly(int numMiles) {} // DOES NOT COMPILE

Again, the parameter list is the same. They must differ.

varargs

public void fly(int[] lengths) {}
public void fly(int... lengths) {} // DOES NOT COMPILE

Remember that Java treats varargs as if they were an array. This means that the method signature is the same for both methods.

Autoboxing

public void fly(int numMiles) {}
public void fly(Integer numMiles) {}

When the primitive version isn’t present, Java will autobox. However, when the primitive int version is provided, there’s no reason for Java to do the extra work of autoboxing.

Reference Types

public class ReferenceTypes {
	public void fly(String s) {
		sout("string");
	}

	public void fly(Object o) {
		sout("object")
	}

	public void main(String[] args) {
		ReferenceTypes r = new ReferenceTypes();
		r.fly("test");
		sout("-");
		r.fly(56)
	}
}

This prints string-object. When it doesn’t find a primitive int version, it autoboxes into Integer, and then it fits as Object.

Primitives

Primitives work in a way that’s similar to reference variables. Java tries to find the most specific matching overloaded method.

public class Plane {
	public void fly(int i) {
		sout("int");
	}

	public void fly(long l) {
		sout("long");
	}

	public static void main(String[] args) {
		Plane p = new Plane();
		p.fly(123);
		sout("-");
		p.fly(123L);
	}
}

This will print int-long. Java can only accept wider types. It won’t automatically convert to a narrower type.

Generics

You might be surprised that these are not valid overloads.

public void walk(List<String> strings) {}
public void walk(List<Integer> integers) {} // DOES NOT COMPILE

Java hasa concept called type erasure, where generics are used only at compile time. This means the compiled code looks like this

public void walk(List strings) {}
public void walk(List integers) {} // DOES NOT COMPILE

Arrays

Unlike the previous example, this code works just fine

public static void walk(int[] ints) {}
public static void walk(Integer[] integers) {}

Encapsulating Data

Encapsulation means only methods in the class with the variables can refer to the instance variables. Callers are required to use these methods.

public class Swan {
	private int eggs; // private

	public int getNumberEggs() { // getter
		return eggs;
	}

	public void setNumberEggs(int newNumber) { // setter
		if(newNumber >= 0) { // guard condition
			eggs = newNumber;
		}
	}
}

Exam Essentials

Be able to identify correct and incorrect method declarations. A sample method declaration is public static void method(String… args) throws Exception {}.

Identify when a method or field is accessible. Recognize when a method or field is accessed when the access modifier (private, protected, public, or default access) does not allow it.

Recognize valid and invalid uses of static imports. Static imports import static members. They are written as import static, not static import. Make sure they are importing static methods or variables rather than class names.

State the output of code involving methods. Identify when to call static rather than instance methods based on whether the class name or object comes before the method. Recognize that instance methods can call static methods and that static methods need an instance of the object in order to call an instance method.

Recognize the correct overloaded method. Exact matches are used first, followed by wider primitives, followed by autoboxing, followed by varargs. Assigning new values to method parameters does not change the caller, but calling methods on them does.

Identify properly encapsulated classes. Instance variables in encapsulated classes are private. All code that retrieves the value or updates it uses methods. These methods are allowed to be public.

Java Fundamentals

Working with Enums

An Enum is a type that can only have a finite set of values. Like a fixed set of contants, but they’re better because they provide type-safe checking.

Creating Simple Enums

public enum Season {
	WINTER, SPRING, SUMMER, FALL
}

How to use an Enum

Season s = Season.SUMMER;
sout(Season.SUMMER); // SUMMER
sout(s == Season.SUMMER); // true

They can be compared using == because they’re like static final constants. You can also use equals() to compare enums.

An enum provides a values() method to get an array of all the values.

for(Season season: Season.values()) {
	sout(season.name() + " " + season.ordinal());
}

This outputs

WINTER 0
SPRING 1
SUMMER 2
FALL 3

You can’t compare an int and enum value directly.

if(Season.SUMMER == 2) // DOES NOT COMPILE

You can retrieve an enum value from a String using valueOf(). The String passed in must match the enum value exactly.

Season s = Season.valueOf("SUMMER"); // SUMMER

Season t = Season.valueOf("summer"); // IllegalArgumentException

You can’t extend an enum

public enum ExtendedSeason extends Season {} // DOES NOT COMPILE

Using Enums in Switch Statements

Season summer = Season.SUMMER;

switch(summer) {
	case WINTER:
		sout("get out the sled");
		break;
	case SUMMER:
		sout("time for the pool");
		break;
	default:
		sout("is it summer yet?");
		berak;
}

Java treats the enum type as implicit. So neither something like Season.FALL nor case 0 would compile. It has to be FALL.

switch(summer) {
	case Season.FALL: // DOES NOT COMPILE
		break;
	case 0: // DOES NOT COMPILE
		break;
}

(!) On the exam, pay special attention when working with enums that they’re used only as enums (!)

Adding Constructors, Fields, and Methods

Enums may hold more than just a list of values.

public enum Season {
	WINTER("Low"),
	SPRING("Medium"),
	SUMMER("High"),
	FALL("Medium");

	private final String expectedVisitors;

	private Season(String expectedVisitors) {
		this.expectedVisitors = expectedVisitors;
	}

	public void printExpectedVisitors() {
		sout(expectedVisitors);
	}
}

The semicolon ; is option just if there’s only a list of values. It’s required if there’s anything else.

All enum constructors are implicitly private, with the modifier being optional. This is reasonable since you can’t extend an enum and the constructor acn be called only within itself. An Enum will not compile if it contains a public or protected modifier.

How to call an enum method.

Season.SUMMER.printExpectedVisitors();

(!) The first time we ask for any enum value, Java constructs all of the enum values. After that, Java just returns the already constructed enum values. (!)

Sometimes, we need to get track of a list of values. We can create an abstract method, and implement it. Every value has to implement it.

public enum Season {
	WINTER {
		public String getHours() {
			return "10am-3pm";
		}
	},
	SPRING {
		public String getHours() {
			return "9am-5pm";
		}
	},
	SUMMER {
		public String getHours() {
			return "9am-7pm";
		}
	},
	FALL {
		public String getHours() {
			return "9am-5pm";
		}
	},
}

If one value doesn’t implement it. It will throw an exception.
If we don’t want each value to implement it, we may create a default implementation and override it only for the special cases.

public enum Season {
	WINTER {
		public String getHours() { return "10am-3pm"; }
	},
	SUMMER {
		public String getHours() { return "9am-7pm"; }
	},
	SPRING,FALL;
	public String getHours() { return "9am-5pm"; }
}

Creating Nested Classes

A nested class is a class that’s defined within another class. It can be:

  • Inner class - A non-static type defined at the member level of a class
  • static nested class - a static type defined at the member level of a class
  • local class - a class defined within a method body
  • anonymous class - a special case of local class that does not have a name.

Nested classes can encapsulate helper classes by restricting them to the containing class. They can make it easy to craete a class that will be used in only one place.

Declaring an Inner Class

An inner class or member inner class is a non-static type defined at the member level of a class. Inner classes have the following properties

  • they can be declared public, protected, default or private
  • they can extend any class and implement interfaces
  • be marked abstract or ginal
  • cannot declare static fields or methods, except for static final fields
  • can access members of the outer class including private members
public class Outer {
	private String greeting = "hi";

	protected class Inner {
		public void go() {
			sout(greeting);
		}
	}

	public void callInner() {
		Inner inner = new Inner();
		inner.go();
	}
}

Since an inner class is not static, it has to be used with an instance of the outer class.
There’s another way to instantiate Inner.

Outer outer = new Outer();
Inner inner = outer.new Inner(); // create the inner class
inner.go();

We need an instance of Outer to create Inner. We can’t just call new Inner(). We call new as if it was a method on the outer variable.

Inner classes can have the same variable names as outer classes.

public class A {
	private int x = 10;

	class B {
		private int x = 20;

		class C {
			private int x = 30;

			public void allTheX() {
				sout(x); // 30
				sout(this.x); // 30
				sout(B.this.x); // 20
				sout(A.this.x); // 10
			}
		}
	}

	public static void main(String[] args) {
		A a = new A();
		A.B b = a.new B();
		A.B.C c = b.new C();
		c.allTheX();
	}
}

Inner Classes Require an Instance

public class Fox {
	private class Den {}

	public void goHome() {
		new Den();
	}

	public static void visitFriend() {
		new Den(); // DOES NOT COMPILE
	}
}

public class Squirrel {
	public void visitFox() {
		new Den(); // DOES NOT COMPILE
	}
}

The first constructor compiles, because goHome() is an instance method.
The second doesn’t because it’s called inside a static method. You can still call the constructor, but you have to explicitly give it a reference to Fox instead.
The last one doesn’t, because it’s not an instance method inside the Fox class. Den is private and only accessible inside Squirrel.

Creating a static Nested Class

A static nested class is a static type defined at the member leve. It can be instantiated without an instance of the enclosing class. It can’t access instance variables or methods in the outer class directly.

It’s like a top-level class except for:

  • It can be made private or use any other access modifiers.
  • The enclosing class can refer to the fields and methods of it
public class Enclosing {
	static class Nested {
		private int price = 6;
	}

	public static void main(String[] args) {
		Nested nested = new Nested();
		sout(nested.price);
	}
}

Since the class is static, you don’t need an instance of Enclosing to use it. You’re also allowed to access private instance variables.

Importing a static Nested Class

You can import it using a regular import, or, since it’s static you can also use a static import.

package bird;
public class Toucan {
	public static class Beak {}
}
package watcher;

import bird.Toucan.Beak;

public class BirdWatcher {
	Beak beak;
}

or

import static bird.Toucan.Beak; // also ok

Writing a Local Class

A local class is a nested class defined within a method. It does not exist until the method is invoked, and it goes out of scope when the method returns. You can create instances only from within the method and you can return those instances.

They can be declared inside methods, constructors and initializers.

They have the following properties:

  • They don’t have access modifiers
  • They cannot be declared static and cannot declare static fields or methods, except for static final
  • They have access to all fields and methods of the enclosing class
  • They can access local variables if the variables are final or effectively final
public class PrintNumbers {
	private int length = 5;

	public void calculate() {
		final int width = 20;

		class MyLocalClass {
			public void multiply() {
				sout(length * width);
			}
		}

		MyLocalClass local = new MyLocalClass();
		local.multiply();
	}

	public static void main(String[] args) {
		PrintNumbers outer = new PrintNumbers();
		outer.calculate();
	}
}

At this example, we can refer to length because it’s effectively final. The following does not compile.

public void processData() {
	final int length = 5;
	int width = 10;
	int height = 2;

	class VolumeCalculator {
		public int multiply() {
			return length * with * height; // DOES NOT COMPILE
		}
	}

	width = 8;
}

Defining an Anonymous Class

An anonymous class is a specialized form of a local class, that does not have a name. It’s declared and instantiated all in one statement. They’re required to extend an existing class or implement an existing interface.
They’re useful when you have a short implementation that will not be used anywhere else.

public class ZooGiftShop {
	abstract class SaleTodayOnly {
		abstract int dollarsOff();
	}

	public int admission(int basePrice) {
		SaleTodayOnly sale = new SaleTodayOnly() {
			int dollarsOff() { return 3; }
		};
		return basePrice - sale.dollarsOff();
	}
}

They can’t both extend a class and implement an interface. You have to choose.

You can define anonymous classes right where they’re needed, even if that’s an argument to a method.

public class ZooGiftShop {
	interface SaleTodayOnly {
		int dollarsOff();
	}

	public int pay() {
		return admission(5, new SaleTodayOnly() {
			public int dollarsOff() { return 3; }
		})
	}

	public int admission(int basePrice, SaleTodayOnly sale) {
		return basePrice - sale.dollarsOff();
	}
}

You can even define anonymous classes outside a method body.

public class Gorilla {
	interface Climb {}
	CLib climbing = new Climb() {};
}

Reviewing Nested Classes

You’ve to learn this tables for the exam, about which syntax rules are permitted in Java.

Permitted Modifiers Inner class static nested class local class anonymous class
Access modifiers all all none none
abstract yes yes yes no
final yes yes yes no
instance methods yes yes yes yes
instance variables yes yes yes yes
static methods no yes no no
static variables yes(if final) yes yes(if final) yes(if final)

You should also know the following information about type of access.

  inner class static nested class local class anonymous class
can extend any class or implement any number of interfaces yes yes yes no - must havev exactly one superclass or one interface
can access instance members of enclosing class without a reference yes no yes (if declared in an instance method) yes (if declared in an instance method)
can access local variables of enclosing method N/A N/A yes (if final or effectively final) yes (if final or effectively final)

Understanding Interface Members

Since Java 8 and 9 four new method types have bee added.

Relying on a default Interface Method

A default method is a method defined in an interface with the default keyword, and includes a method body.
It may be overriden by a class implementing the interface. It’s considered the default implementation.

They were added for backward compatibility. It allows you to add a new method to an existing interface, without the need to modify older code that implements the interface.

public interface IsWarmBlooded {
	boolean hasScales();

	default double getTemperature() {
		return 10.0;
	}
}

(!) Remember: both of these methods include the implicit public modifier, so overriding them with a different access modifier is not allowed. (!)

Default Interface Method Definition Rules
  • It may be declared only within an interface
  • It must be marked with the default keyword and include a method body
  • It’s assumed to be public
  • It cannot be marked abstract, final, or static
  • It may be overridden by a class that implements the interface
  • If a class inherits two or more default methods with the same method signature, then the class must override the method
public interface Carnivore {
	public default void eatMeat(); // DOES NOT COMPILE

	public int getRequiredFood() { // DOES NOT COMPILE
		return 13;
	}
}
Inheriting Duplicate Default Methods
public interface Walk {
	public default int getSpeed() { return 5; }
}

public interface Run {
	public default int getSpeed() { return 10; }
}

public class Cat implements Walk, Run { // DOES NOT COMPILE

}

If a class implements two interfaces that have a default method with the same signature, the compiler will throw an error. The class implementing the interfaces has to override the duplicate default method.

public class Cat implements Walk, Run {
	public int getSpeed() { return 1; }
}
Calling a Hidden default Method

With two inherited default getSpeed() methods. How would you call the version of the default method in the Walk interface?

You can’t call Walk.getSpeed(), as a default method is treated as part of the instance, so they cannot be called like static methods.

You also can’t call super.getSpeed(), because it couldn’t diferentiate between both inherited methods.

The solution is a combination of both of these answers.

public class Car implements Walk, Run {
	public int getSpeed() { return 1; }

	public int getWalkSpeed() {
		return Walk.super.getSpeed();
	}
}

Using static Interface Methods

Java now supports static interface methods.

Static Interface Method Definition Rules
  • It must be marked with static and include a method body
  • A static method without an access modifier is assumed to be public
  • It cannot be marked abstract or final
  • It’s not inherited and cannot be accessed in a class implementing the interface without a reference to the interface name
public interface Hop {
	static int getJumpHeight() {
		return 8;
	}
}
Hop.getJumpHeight();

This is an example for the fourth rule. It implements Hop and does not compile.

public class Bunny implements Hop {
	public void printDetails() {
		sout(getJumpHeight()); // DOES NOT COMPILE
	}
}

Without an explicit reference to the name of the interface, the code will not compile, even though it implements Hop.

public class Bunny implements Hop {
	public void printDetails() {
		sout(Hop.getJumpHeight());
	}
}
Introducing private Interface Methods

Since Java 9, interfaces may now include private interface methods. They’re useful to reduce code duplication for default methods.

They cannot be used outside the interface definition, nor inside static interface methods without a static modifier.

public interface Schedule {
	default void wakeUp() { checkTime(7); }
	default void haveBreakfast() { checkTime(9); }

	private void checkTime(int hour) {
		...
	}
}

Rules:

  • It must be marked with the private modifier and include a method body
  • It may be called only by default and private (non- static) methods within the interface definition

They cannot be declared abstract since they’re not inherited.

Introducing private static Interface Methods

Java 9 added private static interface methods. They can also be accessed by default and private methods.

public interface Swim {
	private static void breathe(String type) {
		...
	}

	static void butterfly() { breathe("butterfly"); }
	public static void freestyle() { breathe("freestyle"); }
	default void backstroke() { breathe("backstroke"); }
	private void breaststroke() { breathe("breastsroke"); }
}

Rules:

  • It must be marked with private and static modifiers and include a method body.
  • It may be called only by other methods within the interface definition.

Introducing Functional Programming

A functional interface is an interface that contains a single abstract method (SAM). They’re the basis for lambda expressions.

Defining a Functional Interface

@FunctionalInterface
public interface Spring {
	public void sprint(int speed);
}

public class Tiger implements Sprint {
	public void sprint(int speed) {
		...
	}
}

This is a functional interface, because it contains exactly one abstract method.

public interface Dash extends Sprint {}

This is a valid functional interface because it inherits and contains a single abstract method.

public interface Skip extends Sprint {
	void skip();
}

This is not because it has two abstract methods. The inherited one and skip()

public interface Sleep {
	private void snore() {}
	default int getZZZ() { return 1; }
}

This isn’t neither because not 1 method match the criteria.

public interface Climb {
	void reach();
	default void fall() {}
	static int getBackUp() { return 100; }
	private static boolean checkHeight() { return true; }
}

This is a functional interface. Despite defining a slew of methods, it contains one abstrac method: reach().

Declaring a Functional Interface with Object Methods

All classes inherit certain methods from Object. For the exam you should be familiar with

String toString();

boolean equals(Object);

int hashCode();

There’s one exception to the single abstract method rule. If a functional interface includes an abstract method with the same signature as a public method found in Object, then those methods do not count towards single abstract method.

public interface Soar {
	abstract String toString();
}

This isn’t a functional interface, since toString() is a public method implemented inside Object.

On the other hand this is a functional interface.

public interface Dive {
	String toString();
	public boolean equals(Object o);
	public abstract int hashCode();
	public void dive();
}

(!) Be wary of examples that resemble methods in the Object class. (!)

public interface Hibernate {
	String toString();
	public boolean equals(Hibernate o);
	public abstract int hashCode();
	public void rest();
}

Hibernate is not a valid interface.

Implementing Functional Interfaces with Lambdas

In addition to functional interfaces you write youtself, Java provides a number of predefined ones.

For example, let’s take a look at Predicate.

public interface Predicate<T> {
	boolean test(T t);
}

We have our own class Animal.

public class Animal {
	private String species;
	private boolean canHop;
	private boolean canSwim;

	// constructor
	// getters
}

Now if we want to check if the animal canHop().

public static void main(String[] args) {
	var animals = new ArrayList<Animal>();
	// add animals

	print(animals, animal -> a.canHop());
}

private static void print(List<Animal> animals,
						Predicate<Animal> checker) {
	for(Animal animal : animals) {
		if(checker.test(animal)) {
			sout(animal + " ");
		}
	}						
}

Writing Lambda Expressions

Normal lambda syntax

a -> a.canHop()

Full lambda syntax

(Animal a) -> { return a.canHop(); }

Ther parentheses can be omitted, only if there’s a single parameter and its type is not explicitly stated. We can also omit braces when we have only a single statement. Java doesn’t require you to type return or use a semicolon when no braces are used.

All the following are valid lambda expressions, assuming that there’re functional interfaces that can consume them.

() -> new Duck();
d -> { return d.quack(); }
(Duck d) -> d.quack()
(Animal a, Duck d) -> d.quack()

The first one can be used by an interface containing a method that takes no args and returns a Duck.
The second and third, both can be used by an interface that takes a Duck as input and returns whatever the type quack() is.
The last can be used by an interface that takes as input Animal and Duck objects and returns the type of quack().

Now let’s check for invalid syntax.

a, b -> a.startsWith("test");        // DOES NOT COMPILE
Duck d -> d.canQuack();              // DOES NOT COMPILE
a -> { a.startsWith("test"); }       // DOES NOT COMPILE
a -> { return a.startsWith("test") } // DOES NOT COMPILE
(Swan s, t) -> s.compareTo(t) != 0   // DOES NOT COMPILE

Lines 1 and 2 require each parentheses around each parameter list. Params are optional only when there’s one param and it doesn’t have a type declared.
Line 3 is missing the return keyword, which is required since we said the lambda must return a boolean.
Line 4 is missing the semicolon ; inside the braces.
Line 5 is missing the param type for t. If the param type is specified for one param, then it must be specified for all of them.

Working with Lambda Variables

Parameter List

Specifying the type of params is optional. Now var can be used in a lambda param list. This means all of this 3 are valid.

Predicate<String> p = x -> true;
Predicate<String> p = (var x) -> true;
Predicate<String> p = (String x) -> true;
Restrictions on Using var in the param list

If var is used for one of the types in the param list, then it must be used for all params in hte list.

(var num) -> 1
(var a, var b) -> "Hello"
(var b, var k, var m) -> 3.14159

var w -> 99 // DOES NOT COMPILE
(var a, Integer b) -> true // DOES NOT COMPILE
(String x, var y, Integer z) -> true // DOES NOT COMPILE

Line 6 does not compile because parentheses are required when using the param name. Line 7 and 8 don’t compile, because the param types include a mix of var and type names.

Local Variables Inside the Lambda Body

It’s legal to define a lambda as a block.

(a, b) -> {
	int c = 0;
	return 5;
}

It’s not allowed to redeclare a variable though.

(a, b) -> {
	int a = 0; // DOES NOT COMPILE
	return 5;
}

(!) Lambda blocks need to end with a semicolon! (!)

public void variables(int a) {
	int b = 1;

	Predicate<Integer> p1 = a -> {
		int c = 0;
		return b == c; } // DOES NOT COMPILE. MISSING ;
}
Variables Referenced from the Lambda Body

Lambda bodies are allowed to use static variables, instance variables, and local variables if they’re effectively final.

public class Crow {
	private String color;

	public void caw(String name) {
		String volume = "loudly";
		Predicate<String> p = s -> (name+volume+color).length() == 10;
	}
}

(!) If the local variable is not final or effectively final, then the code does not compile. (!)

public class Crow {
	private String color;

	public void caw(String name) {
		String volume = "loudly";
		color = "allowed";
		name = "not allowed";
		volume = "not allowed";

		Predicate<String> p =
			s -> (name+volume+color).length()==9; // DOES NOT COMPILE
	}
}

Summary

Exam Essentials

This chapter focused on core fundamentals of the Java language that you will use throughout this book. We started with the final modifier and showed how it could be applied to local, instance, and static variables, as well as methods and classes.

We next moved on to enumerated types, which define a list of fixed values. Like boolean values, enums are not integers and cannot be compared this way. Enums can be used in switch statements. Besides the list of values, enums can include instance variables, constructors, and methods. Methods can even be abstract, in which case all enum values must provide an implementation. Alternatively, if an enum method is not marked final, then it can be overridden by one of its value declarations.

There are four types of nested classes. An inner class requires an instance of the outer class to use, while a static nested class does not. A local class is one defined within a method. Local classes can access final and effectively final local variables. Anonymous classes are a special type of local class that does not have a name. Anonymous classes are required to extend exactly one class by name or implement exactly one interface. Inner, local, and anonymous classes can access private members of the class in which they are defined, provided the latter two are used inside an instance method.

As of Java 9, interfaces now support six different members. Constant variables (static final) and abstract methods should have been familiar to you. Newer member types include default, static, private, and private static methods. While interfaces now contain a lot of member types, they are still distinct from abstract classes and do not participate in the class instantiation.

Last but certainly not least, this chapter included an introduction to functional interfaces and lambda expressions. A functional interface is an interface that contains exactly one abstract method. Any functional interface can be implemented with a lambda expression. A lambda expression can be written in a number of different forms, since many of the parts are optional. Make sure you understand the basics of writing lambda expressions as you will be using them throughout the book.

Exam Essentials

Be able to correctly apply the final modifier. Applying the final modifier to a variable means its value cannot change after it has been assigned, although its contents can be modified. An instance final variable must be assigned a value when it is declared, in an instance initializer, or in a constructor at most once. A static final variable must be assigned a value when it is declared or in a static initializer. A final method is one that cannot be overridden by a subclass, while a final class is one that cannot be extended.

Be able to create and use enum types. An enum is a data structure that defines a list of values. If the enum does not contain any other elements, then the semicolon (;) after the values is optional. An enum can have instance variables, constructors, and methods. Enum constructors are implicitly private. Enums can include methods, both as members or within individual enum values. If the enum declares an abstract method, each enum value must implement it.

Identify and distinguish between types of nested classes. There are four types of nested types: inner classes, static classes, local classes, and anonymous classes. The first two are defined as part of a class declaration. Local classes are used inside method bodies and scoped to the end of the current block of code. Anonymous classes are created and used once, often on the fly. More recently, they are commonly implemented as lambda expressions.

Be able to declare and use nested classes. Instantiating an inner class requires an instance of the outer class, such as calling new Outer.new Inner(). On the other hand, static nested classes can be created without a reference to the outer class, although they cannot access instance members of the outer class without a reference. Local and anonymous classes cannot be declared with an access modifier. Anonymous classes are limited to extending a single class or implementing one interface.

Be able to create default, static, private, and private static interface methods. A default interface method is a public interface that contains a body, which can be overridden by a class implementing the interface. If a class inherits two default methods with the same signature, then the class must override the default method with its own implementation. An interface can include public static and private static methods, the latter of which can be accessed only by methods declared within the interface. An interface can also include private methods, which can be called only by default and other private methods in the interface declaration.

Determine whether an interface is a functional interface. Use the single abstract method (SAM) rule to determine whether an interface is a functional interface. Other interface method types (default, private, static, and private static) do not count toward the single abstract method count, nor do any public methods with signatures found in Object.

Write simple lambda expressions. Look for the presence or absence of optional elements in lambda code. Parameter types are optional. Braces and the return keyword are optional when the body is a single statement. Parentheses are optional when only one parameter is specified and the type is implicit. If one of the parameters is a var, then they all must use var.

Determine whether a variable can be used in a lambda body. Local variables and method parameters must be final or effectively final to be referenced in a lambda expression. Class variables are always allowed. Instance variables are allowed if the lambda is used inside an instance method.

Lambdas and Functional Interfaces

Writing Simple Lambdas

You can think of a lambda expression as an unnamed method. It has parameters and a body, but it doesn’t have a name like a real method.

They’re good to abstract function usage, for places when we want to change just one function for another. They allow os to pass functions as params.

print(animals, a -> a.canHop());

This code uses a concept called deferred execution. This means that code is specified now but will run later.

Lambda Syntax

Lambdas work with interfaces that have only one abstract method.
The syntax is tricky because many parts are optional. These two lines do the exact same thing.

a -> a.canHop();
(Animal a) -> { return a.canHop(); }
  • a single parameter
  • -> arrow operator
  • a.canHop() a body that calls a single method and returns the result of that method

This is a valid lambda

s -> {}

If there’s no code on the right side, you don’t need the semicolon or the return statement.

Examples of valid lambdas with 0, 1 and 2 parameters

() -> true
a -> a.startsWith("test");
(String a) -> a.startsWith("test");
(a, b) -> a.startsWith("test");
(String a, String b) -> a.startsWith("test");

INVALID lambdas

a,b -> a.startsWith("test"); // missing parentheses
a -> { s.startsWith("test"); } // missing return
a -> { return a.startsWith("test") } // missing semicolon

Remember that parentheses are optional only when there’s one parameter and it doesn’t have a type declared.

Introducing Functional Interfaces

A functional interface is an interface with just one abstract method. Lambdas work just with them.

boolean test(Animal a);

This is known as the Single Abstract Method (SAM) rule.
Java provides the @FunctionalInterface annotation to mark interfaces that will be compatible with lambdas. It’s not needed to use them, though. It’s optional.

Predicate

It’s used to test a class usage that we come across. It’s in the package java.util.function.

public interface Predicate<T> {
	boolean test(T t);
}

Example

public class PredicateSearch {
	public static void main(String... args) {
		List<Animal> animals = new ArrayList<Animal>();
		animals.add(new Animal("fish", false, true));

		print(animals, a -> a.canHop());
	}

	private static void print(List<Animal> animals,
							  Predicate<Animal> checker) {
		for(Animal animal : animals) {
			if(checker.test(animal))
				sout(animal + " ");
		}
		soutln();
	}
}

Consumer

It receives a value and returns nothing.

void accept(T t);

A common reason for this is printing a message.

public static void main(String... args) {
	print(x -> sout(x), "Hello word");
}

private static void print(Consumer<String> consumer,
							String value) {
	consumer.accept(value);							
}

Supplier

It receives nothing and returns a value.

T get();

A good use case is when generating values.

public static void main(String... args) {
	Supplier<Integer> random = () -> new Random().nextInt();
	sout(returnNumber(random));
}

private static int returnNumber(Supplier<Integer> supplier) {
	return supplier.get();
}

Comparator

A negative number means the first value is smaller.
Zero means both values are equal.
A positive number means the first value is bigger.

int compare(T o1, T o2);

For the following lambdas

Comparator<Integer> ints = (i1, i2) -> i1 - i2;

The ints comparator uses natural sorf order and follows the above rules. If the first number is bigger, it will return a positive number.

Comparator<String> strings = (s1, s2) -> s2.compareTo(s1);
Comparator<String> moreStrings = (s1, s2) -> - s1.compareTo(s2);

Both of the above comparator do the same thing. Sort in descending order.
The first one is “backwards”, making it descending.
The second uses the default order, however it applies a - sign reversing it.

Working with Variables in Lambdas

Variables can appear in three places with respect to lambdas

Parameter List

Specifying the type of params is optional. Additionally var can be used in place of the specific type.

All these statements are valid and the type of x for all of them is String.

Predicate<String> p = x -> true;
Predicate<String> p = (var x) -> true;
Predicate<String> p = (String x) -> true;

A lambda infers the types from the surrounding context. Another place to look for the type is in method signature.

// the type is Integer
public void whatAmI() {
	consume((var x) -> sout(x), 123)
}

public void consume(Consumer<Integer> c, int num) {
	c.accept(num);
}

You can also determine the type without even seeing the method signature.

// the type is again Integer
public void counts(List<Integer> list) {
	list.sort((var x, var y) -> x.compareTo(y));
}

Local Variables inside the Lambda Body

It’s legal to define a lambda with a block. That block can include local variable declarations.

(a, b) -> {
	int c = 0;
	return 5;
}

When writing your own code, a lambda block with a local variable is a good hint that you should extract that code into a method.

(a, b) -> {
	int a = 0; // DOES NOT COMPILE
	return 5;
}

You cannot redeclare a variable (a) with the name as one already declared in that scope.

11: public void variables(int a) {
12: 	int b = 1;
13: 	Predicate<Integer> p1 = a -> {
14: 		int b = 0;
15: 		int c = 0;
16: 		return b == c;
17: 	}
}

There’re three syntax errors on the above code.
The first on line 13. The variable a was already used in this scope, so it cannot be reused.
The next one comes on line 14 where it attempts to redeclare local variable b.
The third and last one is at line 17. It’s missing a ; to close the Predicate block.

(!) Beware with missing semicolons for lambdas. They’re tricky (!)

Variables Referenced from the Lambda Body

Lambda bodies are allowed to reference some vars from the surrounding code.

public class Crow {
	private String color;

	public void caw(String name) {
		String volume = "loudly";
		Consumer<String> consumer = s -> sout(name + color);
	}
}

Lambdas can access an instance variable, method parameter or local variable under certain conditions.

Instance and class variables are always allowed.
Method parameters and local variables are allowed to be referenced if they’re effectively final. This means that the value doesn’t change after it’s set, regardless of whether it’s explicitly marked as final. If a local variable had it’s value changed, it cannot be used by a lambda.

Rules for accessing a variable from a lambda body inside a method

Variable type Rule
Instance variable Allowed
Static variable Allowed
Local variable Allowed if effectively final
Method variable Allowed if effectively final
Lambda variable Allowed

Calling APIs with Lambdas

These are the most common methods that use lambdas.

removeIf()

List and Set declare a removeIf() method that takes a Predicate.

List<String> bunnies = new ArrayList<>();
bunnies.add("long ear");
bunnies.add("floppy");
bunnies.add("hoppy");
bunnies.removeIf(s -> s.charAt(0) != 'h');

sort()

While you can call Collections.sort(list), you can now also sort directly on the list.

List<String> bunnies = new ArrayList<>();
bunnies.add("long ear");
bunnies.add("floppy");
bunnies.add("hoppy"); // [long ear, floppy, hoppy]
// list alphabetically
bunnies.sort((b1, b2) -> b1.compareTo(b2)); // [floppy, hoppy, long ear]

There’s not a sort method on Set or Map! Neither of those types has indexing.

forEach()

It takes a Consumer and calls that lambda for each element encountered.

List<String> bunnies = new ArrayList<>();
bunnies.add("long ear");
bunnies.add("floppy");
bunnies.add("hoppy");
bunnies.forEach(b -> sout(b)); // long ear
							   // floppy
							   // hoppy

For a Set it works the same as a List.
For a Map you have to choose whether you want to go through the keys or values.

Map<String, Integer> bunnies = new HashMap<>();
bunnies.put("long ear", 3);
bunnies.put("floppy", 8);
bunnies.put("hoppy", 1);
bunnies.keySet().forEach(b -> sout(b));
bunnies.values().forEach(b -> sout(b));
// it's also possible to use forEach directly with a Map
bunnies.forEach((k,v) -> sout(k + " " + v));

Summary

Lambda expressions, or lambdas, allow passing around blocks of code. The full syntax looks like this:

(String a, String b) -> { return a.equals(b); }

The parameter types can be omitted. When only one parameter is specified without a type the parentheses can also be omitted. The braces and return statement can be omitted for a single statement, making the short form as follows:

a -> a.equals(b)

Lambdas are passed to a method expecting an instance of a functional interface. A functional interface is one with a single abstract method. Predicate is a common interface that returns a boolean and takes any type. Consumer takes any type and doesn’t return a value. Supplier returns a value and does not take any parameters. Comparator takes two parameters and returns an int.

A lambda can define parameters or variables in the body as long as their names are different from existing local variables. The body of a lambda is allowed to use any instance or class variables. Additionally, it can use any local variables or method parameters that are effectively final.

We covered three common APIs that use lambdas. The removeIf() method on a List and a Set takes a Predicate. The sort() method on a List interface takes a Comparator. The forEach() methods on a List and a Set interface both take a Consumer.

Exam Essentials

Write simple lambda expressions. Look for the presence or absence of optional elements in lambda code. Parameter types are optional. Braces and the return keyword are optional when the body is a single statement. Parentheses are optional when only one parameter is specified and the type is implicit.

Identify common functional interfaces. From a code snippet, identify whether the lambda is a Comparator, Consumer, Predicate, or Supplier. You can use the number of parameters and return type to tell them apart.

Determine whether a variable can be used in a lambda body. Local variables and method parameters must be effectively final to be referenced. This means the code must compile if you were to add the final keyword to these variables. Instance and class variables are always allowed.

Use common APIs with lambdas. Be able to read and write code using forEach(), removeIf(), and sort().

Core Java APIs

Creating and Manipulating Strings

A String is a fundamental reference type.
It implements the interface CharSequence. This interface is a general way of representing several classes, including String and SringBuilder.

These two snippets both create a String.

String name = "Fluffy";
String name = new String("Fluffy");

Concatenation

The exam creatos like String concatenation because the + operator can be used in two ways within the same line of code.

Rules:

  1. If both operands are numeric + means numeric addition
  2. If either operand is a String + means concatenation
  3. The expression is evaluated from left to right
System.out.println(1 + 2); // 3
System.out.println("a" + "b"); // ab
System.out.println("a" + "b" + 3); // ab3
System.out.println(1 + 2 + "c"); //3c
System.out.println("c" + 1 + 2); //c12

If you see questions like this, just take your time and check the types.

int three = 3;
String four = "4";
System.out.println(1 + 2 + three + four);
String s = "1";
s += "2";
s += 3;
System.out.println(s);

Immutability

A String is immutable. Once a String object is created, it’s not allowed to change. It cannot be made larger nor smaller, and you cannot change one of the characters inside it.

String s1 = "1";
String s2 = s1.concat("2");
s2.concat("3");
System.out.println(s2); // prints "12" because String is immutable

Important String Methods

For all these methods, you need to remember that a String is a sequence of characters anda Java counts from 0 when indexed.

length()

int length();

It returns the number of characters in the String.

String string = "animals";
System.out.println(string.length()); // 7

Strings counts from 0, only when you’re using indexes or positions within a list. When determining the total size or length, Java uses normal counting.

charAt()

char charAt(int index);

It lets you query the String to find out what character is at a specific index.

String string = "animals";
System.out.println(string.charAt(0)); // a
System.out.println(string.charAt(6)); // s
System.out.println(string.charAt(7)); // throws Exception

indexOf()

int indexOf(int ch);
int indexOf(int ch, int fromIndex);

int indexOf(String str);
int indexOf(String str, int fromIndex);

It looks at the chars in the string and finds the first index that matches the desired value.

It can work with an individual char or a whole String as input.
It can also start from a requested position.

String string = "animals";
System.out.println(string.indexOf('a')); // 0
System.out.println(string.indexOf("al")); // 4
System.out.println(string.indexOf('a', 4)); // 4
System.out.println(string.indexOf("al", 5)); // -1

It returns -1 when no match is found.

substring()

String substring(int beginIndex);
String substring(int beginIndex, int endIndex);

It also looks for charactes in a String. It returns part of the String. The first parameter is the index to start with for the returned String. This is zero-based index. There’s an optional second parameter which is the index you want to stop at. It’s “stop at”, not “include”. The endIndex parameter is allowed to be 1 past the end of the sequence if you want to stop at the end of the sequence. That woul be redundant though since you could omit the second parameter in that case. Don’t be surprised if the exam uses it.

The method returns the string starting from the requested index. If an index is requested, it stops right before that index. Otherwise, it goes to the end of the String.

String string = "animals";
System.out.println(string.substring(3)); // mals
System.out.println(string.substring(string.indexOf('m'))); // mals
System.out.println(string.substring(3, 4)); // m
System.out.println(string.substring(3, 7)); // mals
System.out.println(string.substring(3, 3)); // empty String
System.out.println(string.substring(3, 2)); // throws Exception
System.out.println(string.substring(3, 8)); // throws Exception

The substring() method is the trickets String method on the exam.

toLowerCase() and toUpperCase()

String toLowerCase();
String toUpperCase();

These methods do exactly what they say. They convert any lowercase characters to uppercase and viceversa.

String string = "animals";
System.out.println(string.toUpperCase()); // ANIMALS
System.out.println("Abc123".toLowerCase()); // abc123

equals() and equalsIgnoreCase()

boolean equals(Object obj);
boolean equalsIgnoreCase(String str);

The method equals() check whether two String objects contain exactly the same characters in the same order. It takes an Object instead of an String. This is because the method is the same for all objects. If you pass something that isn’t a String, it will just return false.

The method equalsIgnoreCase() checks whether two String objects contain the same characters without considering characters’ cases.

System.out.println("abc".equals("ABC"));  // false
System.out.println("ABC".equals("ABC")); // true
System.out.println("abc".equalsIgnoreCase("ABC")); // true

startsWith() and endsWith()

boolean startsWith(String prefix);
boolean endsWith(String suffix);

They look at whether the provided valeu matches part of the String.

System.out.println("abc".startsWith("a")); // true
System.out.println("abc".startsWith("A")); // false
System.out.println("abc".endsWith("c")); // true
System.out.println("abc".endsWith("a")); // false

replace()

String replace(char oldChar, char newChar);
String replace(CharSequence target, CharSequence replacement);

It does a simple search and replace on the String. There’s a version that takes char parameters as well as a version that takes CharSequence parameters.

System.out.println("abcabc".replace('a', 'A')); // AbcAbc
System.out.println("abcabc".replace("a", "A")); // AbcAbc

contains()

boolean contains(CharSequence charSeq);

It looks for matches in the String. The match can be anywhere in the String. It’s case sensitive.

System.out.println("abc".contains("b")); // true
System.out.println("abc".contains("B")); // false

trim(), strip(), stripLeading(), stripTrailing()

String strip();
String stripLeading();
String stripTrailing();

String trim();

Removes blank space from the begginning and/or end of a String. strip() and trim() remove whitespace from the beginning and end of a String. Whitespace consists of spaces along with the \t, \r and \n characters.

The strip() method is new in Java11. It does everything that trim() does, but it supports Unicode.

Additionally, stripLeading() and stripTrailing() methods were added in Java11. stripLeading() method removes whitespace from the beginning of the String and leaves it at the end. The stripTrailing()method does the opposite. It removes from the end and leaves it at the beginning.

System.out.println("abc".strip()); // abc
System.out.println("\t a b c\n".strip()); // a b c

String text = " abc\t ";
System.out.println(text.trim().length()); // 3
System.out.println(text.strip().length()); // 3
System.out.println(text.stripLeading().length()); // 5
System.out.println(text.stripTrailing().length()); // 4

Remember \t is a single character.

intern()

String intern();

It returns the value from the string pool if it’s there. Otherwise it adds the value to the string pool.

Method Chaining

On the exam there’s a tendency to cram as much code as possible into a small space. You’ll see a lot of method chaining such as

String result = "AniMaL     ".trim().toLowerCase().replace('a', 'A');
System.out.println(result);

Remember that String is immutable.

String a = "abc";
String b = a.toUpperCase();
b = b.replace("B", "2").replace('C', '3');
System.out.println("a=" + a); // "abc"
System.out.println("b=" + b); // "A23"

Using the StringBuilder Class

String is immutable. For every iteration of the following loop, a new String object is created and the old one becomes eligible for garbage collection. After 26 iterations, a total of 27 objects are instantiated.

String alpha = "";
for(char current = 'a'; current <= 'z'; current++)
	alpha += current;
sout(alpha);

This is a far better solution.

StringBuilder alpha = new StringBuilder();
for(char current = 'a'; current <= 'z'; current++)
	alpha.append(current);
sout(alpha);

Mutability and Chaining

StringBuilder is not immutable. The exam will likely try to trick you with respect to String and StringBuilder being mutable.

When we chain String method calls, the result is a new String with the answer. StringBuilder changes its own state and returns a reference to itself.

1: StringBuilder sb = new StringBuilder("start");
2: sb.append("+middle"); // sb = "start+middle"
3: StringBuilder same = sb.append("+end"); "start+middle+end"

Line 2 adds text to the end of sb and returns a reference, which is ignored. Line 6 also adds text to the end of sb a returns a reference, which is stored in same. Which means sb and same point to the same object and would print the same.

StringBuilder a = new StringBuilder("abc");
StringBuilder b = a.append("de");
b = b.append("f").append("g");
sout(a);
sout(b);

At this example, there’s only one object. The result is both point to the same object and both print abcdefg.

Creating a StringBuilder

There’re three ways to construct a StringBuilder.

StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder("animal");
StringBuilder sb3 = new StringBuilder(10);

The third one tells Java that we have some idea of how big the eventual value will be, and how StringBuilder should reserve a certain capacity for it.

Important StringBuilder Methods

charAt(), indexOf(), length(), substring()

These four methods work exactly the same as for String.

StringBuilder sb = new StringBuilder("animals");
String sub = sb.substring(sb.indexOf("a"), sb.indexOf("al"));
int len = sb.length();
char ch = sb.charAt(6);
sout(sub + " " + len + " " + ch);

The correct answer is anim 7 s.
indexOf() return 0 and 4. substring() return the String starting with index 0 and ending right before index 4. length() returns 7 because it’s the number of chars rather than an index. charAt() returns the char at index 6. Here we do start with 0 because we’re referring to indexes.
Notice that substring() returns a String rather than a StringBuilder. It’s a method that inquires about what the state of the StringBuilder happens to be.

append()

StringBuilder append(String str); // there're more for more data types.

This is the most frequently used method. It does what it soulds like. It adds the param to the StringBuilder and returns a reference to the current StringBuilder.

insert()

StringBuilder insert(int offset, String str); // there're more for more data types.

It adds chars to the StringBuilder at the requested index and returns a reference to the current StringBuilder.

StringBuilder sb = new StringBuilder("animals");
sb.insert(7, "-"); // animals-
sb.insert(0, "-"); // -animals-
sb.insert(4, "-"); // -ani-mals-

(!) The exam creators will try to trip you up on this. As we add and remove chars, their indexes change. When you see a question such as this, draw what’s going on so you won’t be confused. (!)

delete() and deleteCharAt()

StringBuilder deleteCharAt(int index);
StringBuilder delete(int startIndex, int endIndex);

This is the opposite of insert(). It removes chars from the sequence and returns a reference to the current StringBuilder. This method is convenient when you want to delete only one character.

StringBuilder sb = new StringBuilder("abcdef");
sb.delete(1, 3); // adef
sb.deleteCharAt(5); // throws an exception.

The delete() method is more flexible than some others when it comes to array indexes. If you specify a second param that’s past the end of the StringBuilder, java will assume you meant the end.

This is valid code.

StringBuilder sb = new StringBuilder("abcdef");
sb.delete(1, 100); // a

replace()

StringBuilder replace(int startIndex, int endIndex, String newString);

This method works differently for StringBuilderthan for String.

StringBuilder builder = new StringBuilder("pigeon dirty");
builder.replace(3, 6, "sty");
sout(builder); // pigsty dirty

First, Java deletes ther chars starting with index 3 and ending right before index 6. Then it inserts the value of sty.

StringBuilder builder = new StringBuilder("pigeon dirty");
builder.replace(3, 100, "");
sout(builder);

This prints pig. The method first does a delete and replace() allows specifying a second param that’s past the end of the StringBuilder.

reverse()

StringBuilder reverse();

It does exactly what it sounds like. It reverses the chars sequence and returns a reference to the current StringBuilder.

StringBuilder sb = new StringBuilder("ABC");
sb.reverse();
sout(sb);

This will print CBA.

toString()

String toString();

This converts a StringBuilder into a String.

StringBuilder sb = new StringBuilder("ABC");
String s = sb.toString();

Understanding Equality

Comparing equals() and ==

StringBuilder one = new StringBuilder();
StringBuilder two = new StringBuilder();
StringBuilder three = one.append("a");
sout(one == two);  // false
sout(one == three); // true

Since we aren’t dealing with primitives, == checks for references. The last check works as StringBuilder methods like to return the current reference for chaining.

String x = "Hello World";
String z = "Hello World".trim();
sout(x.equals(z)); // true

This checks the inside of the String rather than the reference itself. If a class doesn’t have an equals method, Java determines whether the references point to the same object - which is exactly what == does. StringBuilder does not implement equals() so when using it, it checks for reference instead of content.

String string = "a";
StringBuilder builder = new StringBuilder("a");
sout(string == builder); // DOES NOT COMPILE. NOT SAME TYPE.

The String Pool

Since String are everywhere, they use up a lot of memory. As many Strings repeat in the program, java reuses common ones. The string pool, also known as the intern pool, is a location in the JVM that collects all these Strings.

Let’s visit a more complex and confusing scenario, String equality, made so in part because of the way the JVM reuses String literals.

String x = "Hello World";
String y = "Hello World";
System.out.println(x == y); // true

Strings are immutable and literals are pooled. The JVM created only one literal in memory and both x and y point to the same location in memory.
It gets trickier.

String x = "Hello World";
String y = new String("Hello World");
sout(x == y); // false

(!) Here the former says to use the String pool normally, but the second option with an explicit new says to not use the string pool amd create a new object, even if it’s less eficient. (!)

String x = "Hello World";
String z = " Hello World".trim();
sout(x == z); // false

In this example, we don’t have two of the same String literal as one is computed at runtime and the other at compile-time.

You can also do the opposite and tell Java to use the String pool. This is done with the intern() method. It will use an object from the pool if one is present. It it’s not present, Java will add it this time.

String name = "Hello World";
String name2 = new String("Hello World").intern();
sout(name == name2); // true

Understanding Java Arrays

String and StringBuilder are implemented using an array of characters. An array is an area of memory on the heap with space for a designated number of elements. An array is an ordered list. It can contain duplicates.

StringBuilder is implemented as an array where the array object is replaced with a new bigger array object when it runs out of space to store characters.

Creating an Array of Primitives

This is most common way to create an array.

int[] numbers1 = new int[3];

When you use this form to instantiate an array, all elements are set to default value for that type.
The indexes start with 0 and count up, just as they did for a String.

Another way to create an array, is to specify all the elements it should start out with

 int[] numbers2 = new int[] {42, 55, 59}; // this is redundant
 int[] numbers2 = {42, 55, 59}; // anonymous array

Beware multiple “Arrays” in declaration

 // this declares two int arrays
 int[] ids, types;

 // this declares ONE int array and ONE int
 int ids[], types;

Creating an Array with Reference Variables

 String[] bugs = { "cricket", "beetle" };
 sout(bugs.toString()); // [Ljava.lang.String;@160bcc9]

The .equals() method on arrays does not look at the elements of the array.
The .toString() method shows the following. [L means it’s an array. java.lang.String is the reference type and 160bcc9 is the hascode.

(!) The array does not allocate space for the String objects. Instead, it allocates space for a reference to where the objects are really stored. (!)

 String[] strings = { "stringValue" };
 Object[] objects = strings;
 String[] againString = (String[]) objects;

 againString[0] = new StringBuilder(); // DOES NOT COMPILE
 objects[0] = new StringBuilder(); // (!)

The last line will compile, but will throw a runtime error (ArrayStoreException).

Using an Array

 String[] mammals = {"monkey", "chimp", "donkey"};
 sout(mammals.length);
 sout(mammals[0]);
 sout(mammals[1]);
 sout(mammals[2]);

(!) Watch out for ArrayIndexOutOfBoundsException. (!)

 int numbers = new int[10];
 // this line tries to access 10 indexes. only 0-9 are valid!
 for(int i = 0; i<= numbers.length; i++)
 	numbers[i] = i + 5;

Sorting

You can pass almost any array to Arrays.sort().