자바(Java)의 정적 유틸리티 클래스
자바에서 정적 유틸리티 클래스란
'객체 상태 정보가 없고, 정적 함수만을 제공하는 클래스'
정적 유틸리티 클래스가 존재하는 이유는 자바에서는 모든 함수가 클래스 내부에 있어야 하기 때문 입니다
ControlJavaActivity.java
Toast.makeText(getApplicationContext(), "" + number + " 는 2의 배수입니다.".Toast.LENGTH_SHORT).show();
ControlKotlinActivity.kt
Toast.makeText(applicationContext, "${number} 는 2의 배수입니다.".Toast.LENGTH_SHORT).show()
토스트 메시지를 호출하는 코드가 너무 길지 않나요?
'메세지 내용'과 '얼마나 길게 표시하는지'의 정보일 것 입니다
토스트 메세지는 코드 전체에서 사용하는 코드로 좀 더 간편하게 사용 할 수 있도록 함수를 만들어보죠!
ControlJavaActivity.java
// 짧은 토스트 메세지를 보여주는 함수
public void toastShort(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
// 긴 토스트 메세지를 보여주는 함수
public void toastLong(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
토스트 메세지를 간편하게 사용하기 위해 함수를 추가해 봅시다
OnClickListener
@Override
public void onClick(View view) {
int number = Integer.parseInt(numberField.getText().toString());
// if, else if, else 문으로 2의 배수, 3의 배수를 체크해 서로 다른 토스트 메세지를 보여준다
if (number % 2 == 0) {
toastShort("" + number + " 는 2의 배수입니다.");
} else if (number % 3 == 0) {
toastShort("" + number + " 는 3의 배수입니다.");
} else {
toastShort("" + number);
}
코드가 이전보다 훨씬 간결해지고 가독성이 높아졌습니다
만약 긴 토스트 메세지를 출력하고 싶다면 'toastLong()' 함수를 같은 방법으로 사용하면 됩니다
그런데 한가지 문제점이 발생합니다
ControlJavaActivity 클래스 내부 함수를 호출해 사용 가능하지만
외부 ControlJavaActivity 클래스 객체를 'new'로 생성 후 호출해야 합니다
토스트 메시지와 ControlJavaActivity는 별 연관성이 없기에 토스트 메세지를 조금 편하게 쓰겠다고 ControlJavaActivity를 new로 생성하는 것은 효율성도 떨어질 뿐이며 직관적이지도 않습니다
다른 여러 클래스에서도 사용토록 'toastShort(), toastLong()' 함수를 다른 클래스로 옮겨야 할 것 같습니다
자바에서 '함수가 반드시 클래스 내부에' 이썽야 하기 때문에 먼저 클래스를 만들어야 합니다
토스트 메세지를 위한 클래스이므로 'ToastUtilJava' 클래스라고 이름 짓겠습니다
[New > Java Class] 선택
Name을 'ToastUtilJava'라고 엔터 해줍니다!
새로 생성된 클래스에 toastShort() 함수와 toastLong() 함수를 복사 붙여 넣어 봅시다!
ToastUtilJava.java
package com.example.kotlinsample.ui.theme;
import android.widget.Toast;
public class ToastUtilJava {
// 짧은 토스트 메세지를 보여주는 함수
public void toastShort(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
// 긴 토스트 메세지를 보여주는 함수
public void toastLong(String message) {
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
}
}
앗!
오류 발생!
'getApplicationContext()' 메소드는 Activity에서 정의된 메소드이기 때문에
상속 받지 않은 ToastUtilJava 클래스는 사용이 불가능 한것이죠
그렇다고 클래스에 상속 받도록 만든다면 토스트 메시지 생성 때 마다 새 Activity를 생성하는 꼴이라서 불필요한 메모리를 사용하게 되죠
이런 경우 Application 클래스를 상속받은 클래스로 만들고 해당 클래스에 applicationContext를 제공하는 것이 하나의 방법이 됩니다
실제 많은 안드로이드 앱 코드에서 사용하기도 합니다
먼저 'MainApplication' 클래스부터 만들겠습니다
방법은 동일합니다
그럼 이제 코드를 채워 넣어야겠지요?
MainApplication.java
package com.example.kotlinsample;
import android.app.Application;
import android.content.Context;
// Application 클래스를 상속받는다
// Application 클래스는 앱이 실행될때 가장 먼저 실행되며 한개의 인스턴스만 존재한다
// 앱의 전역적으로 사용하는 상태 정보를 관리하는 기본 클래스
public class MainApplication extends Application {
// applicationContext 를 바인딩한다
private static Context applicationContext;
// applicationContext 를 전역적으로 제공할 메소드
public static Context getAppContext() {
return applicationContext;
}
// 앱이 최초 실행될때 호출된다
@Override
public void onCreate() {
super.onCreate();
// static 으로 선언된 applicationContext 에 현재 실행중인
// applicationContext 로 바인딩한다
applicationContext = getApplicationContext();
}
}
실행하는 Application 클래스를 바꾸려면 AndroidManifest.xml 파일에 명시적으로 지정합니다
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.kotlinsample">
<!-- INTERNET 권한을 요청합니다. -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MainApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.KotlinSample"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="com.example.kotlinsample.MainActivity"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".BmiKotlinActivity"
android:label="com.example.kotlinsample.BmiKotlinActivity"
android:theme="@style/Theme.AppCompat.Light" />
<activity
android:name=".BmiJavaActivity"
android:label="com.example.kotlinsample.BmiJavaActivity"
android:theme="@style/Theme.AppCompat.Light" />
<activity
android:name=".ControlKotlinActivity"
android:label="ControlKorlin"
android:theme="@style/AppTheme"
android:exported="false" />
<activity
android:name=".ControlJavaActivity"
android:label="ControlJava"
android:theme="@style/AppTheme"
android:exported="false" />
<activity
android:name=".VariableKotlinActivity"
android:exported="false"
android:label="VariableKotlin"
android:theme="@style/AppTheme" />
<activity
android:name=".VariableJavaActivity"
android:exported="false"
android:label="VariableJava"
android:theme="@style/AppTheme" />
</application>
</manifest>
<application> 태그를 볼까요?
<application
android:name=".MainApplication"
... 생략
>
'android:name' 속성은 앱에서 사용할 Application 클래스를 지정하는 속성
방금 전 생성한 MainApplication 클래스를 지정하였습니다
앱 실행 중 반드시 하나의 인스턴스 존재로
Application 클래스는 applicationContext의 값이 'NULL'이 아닌 것을 보장하는 것입니다
ToastUtilJava
package com.example.kotlinsample;
import android.widget.Toast;
public class ToastUtilJava {
// 짧은 토스트 메세지를 보여주는 함수
public void toastShort(String message) {
Toast.makeText(MainApplication.getAppContext(), message, Toast.LENGTH_SHORT).show();
}
// 긴 토스트 메세지를 보여주는 함수
public void toastLong(String message) {
Toast.makeText(MainApplication.getAppContext(), message, Toast.LENGTH_LONG).show();
}
}
발생하던 오류가 없어졌죠?
이제 드디어 어떤 클래스에서도 전역적으로 편하게 토스트 메세지를 띄울 수 있도록 되었습니다
토스트 메세지를 사용하고 싶다면 어느 클래스에서라도 다음 코드를 타이핑하면 되겠죠?
new ToastUtilJava().toastShort("message");
그런데 ToastUtilJava 클래스를 new 키워드로 생성해야 할까요?
클래스 생성 시 객체 상태를 저장하는 속성이 없어서
굳이 new 키워드로 '인스턴스화'하는 의미가 없습니다
이런 경우 'static'를 이용해 정적인 함수로 만드는게 좋죠
ToastUtilJava.java 클래스 수정
package com.example.kotlinsample;
import android.widget.Toast;
public class ToastUtilJava {
// 짧은 토스트 메세지를 보여주는 함수
public static void toastShort(String message) {
Toast.makeText(MainApplication.getAppContext(), message, Toast.LENGTH_SHORT).show();
}
// 긴 토스트 메세지를 보여주는 함수
public static void toastLong(String message) {
Toast.makeText(MainApplication.getAppContext(), message, Toast.LENGTH_LONG).show();
}
}
이제는 불필요 객체 생성할 필요가 없고
호출 시 코드도 더 간결해집니다
이제 모든 클래스에서
ToastUtilJava.toastShort("message");
이렇게 '객체의 상태 정보가 없고 정적인' 메소드를 모아둔 클래스를
'정적 유틸리티 클래스'라고 부릅니다
프로젝트 진행 시 보통 정적 유틸리티 클래스가 생기는데 방금 ToastUtilJava 클래스 처럼
전역적으로 공통 유틸리티 함수를 사용하는 경우가 꽤 많습니다
한가지 불편한 점은 이런 정적 함수들은 굳이 '클래스'가 필요치 않음에도 불구하고
클래스 형태를 만들어야 한다는 것 입니다
왜냐면 Java는 '모든 함수가 클래스 내부에 있어야 하기 때문' 입니다
하지만 이제 정적 유틸리티 클래스 생산이 필요치 않습니다
코틀린은 최상위 함수를 제공해 유틸리티 클래스 없이 전역적 함수 접근을 제공합니다