본문 바로가기

수업 정리

71일차 수업정리(Android - Toast, Dialog, EventHandling)

**Toast

    =>짧은 문자열을 출력하는 작은 대화상자

    => 안드로이드 시스템이 제공 : 앱이 종료되도 출력 가능

    => 플로팅(화면에 떠있는 상태) 형태로 화면 하단에 나타났다가 일정 시간이 지나면 자동으로 사라짐

    => 알림 사항을 전달만 하고 포커스를 받을 수가 없음

1. 생성

Toast.makeText(Context context, int resid, int duration)

Toast.makeText(Context context, CharSequence text, int duration)

    => Context는 Toast는 화면에 출력되는 요소이기 때문에 화면 출력정보를 소유한 Context를 매개변수로 받아야 함

        - Context는 화면에 무엇인가를 출력할 때 사용하기 위한 선, 면, 글자, 배경 색상등의 정보를 소유한 객체

        - 안드로이드에서는 직접 Context를 생성하는 경우는 거의 없고 Context를 상속 받은 Activity를 이용하여 화면 출력을 함

        - Activity에 대한 참조를 얻는 방법은 Activity 클래스 안에서는 this나 Activity.this를 이용

        - 이벤트 처리시 anonymous class를 자주 사용, anonymous class는 Activity.this를 이용하여 Activity클래스 객체에 대한 참조 가능

public class SampleActivity{
	public void onCreate(){
    	//this나 SampleActivity/this나 모두 현재 인스턴스에 대한 참조
        btn.setOnClickListener(new View.OnClickListener(){
        	//this는 View.OnClickListener의 인스턴스에 대한 참조
            //SampleActivity.this가 SampleActivity의 인스턴스에 대한 참조
        });
    }
}

 

    => 안드로이드에서는 getApplicationContext()라는 메소드를 호출하면 시작 Activity에 대한 포인터를 리턴받을수 있음

    => 두번째 매개변수는 출력할 메시지로 string.xml파일에 리소스로 등록하고 그 리소드 id를 설정해도 되고 직접 문자열로 설정해도 됨

        - 안드로이드는 String도 많이 사용하지만 String의 상위 인터페이스 문자열 리턴시 리턴문자열의 자료형을 정확하게 파악하여

         CharSequence나 Editable이면 toString()을 호출하여 문자열로 변환하여 사용

    => 세번째 매개변수는 토스트를 출력할 시간인데 1/1000초 단위로 설정가능(대부분 Toast.SHORT, Toast.LONG등의 상수 사용)

 

2. 출력

    => Show()를 호출

 

3. 기타 메소드

    => setGravity

    => setMargin

    => setText

    => setDuration

    => cancel

    => setView(View view) : View를 설정 - 모양 변경

 

4. 실습

    => 버튼을 누르면 토스트가 출력되도록 하기

  1) 프로젝트를 생성하거나 실행 가능한 Activity를 추가

  2) 레이아웃에 버튼을 1개 추가

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="토스트 출력"
        android:id="@+id/btnToast"/>

</LinearLayout>

 

  3) Activity.java 파일의 onCreate 메소드 수정

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //xml애 디자인한 뷰 객체 찾아오기
        Button btn = (Button)findViewById(R.id.btnToast);
        //버튼 클릭시 이벤트
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //토스트 만들기
                Toast toast = Toast.makeText(MainActivity.this, "토스트를 출력합니다", Toast.LENGTH_LONG);
                //토스트를 화면에 출력
                toast.show();
            }
        });
    }

 

**Snackbar

    => 하단에 고정된 위치에 메시지를 출력해주는 UI

    => 초창기 안드로이드 버전에 없었고 나중에 추가 됨(라이브러리를 추가하여 사용해야 함)

1. 안드로이드에서 추가된 라이브러리 사용

    => 안드로이드는 버전이 업데이트 될때 새로운 UI를 추가하는 경우가 많아 이런 UI사용시 사용

  1) File - Project Structure메뉴를 실행

  2) 왼쪽 창에서 Modules 하단의 app을 선택

  3) 오른쪽 탭에서 dependencies를 선택하고 + 버튼을 눌러 필요한 라이브러리 추가

    => Snackbar는 support.design 라이브러리를 추가하여 사용

 

2. Snackbar

    => Snackbar는 Action을 통해서 onClick을 설정할 수 있음

        - 사용자와의 인터페이스로 이용가능

    => Toast는 Context를 매개변수로 받지만 Snackbar는 View를 매개변수로 받음

  1) 생성

    - Snackbar.make(View view, CharSequence text, int duration)

    - Snackbar.make(View view, int residents, int duration)

  2) 출력

    - show()

  3) 클릭했을 때 동작 지정

setAction(String msg, new View.onClickListener(){
	public void onClick(View view){
    //클릭시 수행할 내용
});

 

3. 버튼 클릭시 Snackbar 출력하기

  1) support.design 라이브러리를 추가

    =>[File] - [ProjectStructure]를 실행

  2) dependecies나 module을 선택하고, dependency위의  +버튼을 눌러서 design검색후 support.design을 추가

 

  3) Activity.java 클래스의 onCreate 메소드에서 버튼의 클릭 처리 메소드의 내용을 수정 - 음악재생을 할려면 res 디렉토리에 raw 디렉토리를 만들고 그 안에 space.mp3 파일을 저장하고 해야 됩니다.

     //버튼 클릭했을 때 처리
        btn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view){
                /*
                //토스트 만들기
                Toast toast = Toast.makeText(
                        MainActivity.this,
                        "토스트를 출력합니다.",
                        Toast.LENGTH_LONG);
                //토스트를 화면에 출력
                toast.show();
                */

                //스낵바 출
                Snackbar.make(view, "스낵바 출력",
                        Snackbar.LENGTH_LONG)
                        .setAction("SONG",
                                new View.OnClickListener(){
                            public void onClick(View view){
                                MediaPlayer player =
                                        MediaPlayer.create(
                                                MainActivity.this,
                                                R.raw.spice);
                                player.start();
                            }
                        })
                        .show();
            }
        });

 

**대화상자 - Dialog

    => 사용자에게 전달 사항을 알리고 사용자의 선택을 받아들이는 통신 수단 중의 하나

    => Activity를 화면에 그대로 유지한 채 중앙에 열리므로 메시지를 보여주거나 추가 입력, 선택용으로 사용하기에 적합

1. 생성 및 출력

  1) Dialog생성

    => 생성자에 Context를 대입 받아서 생성

  2) 모양 설정

    - setContextView(View view)로 출력되는 모양 설정

    - setTitle(문자열)을 이용해서 제목을 설정

  3) 출력

    - show()

 

2. 일반 다이얼로그 출력

  1) 사용가능한 Activity 추가

  

  2) 레이아웃 수정

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="대화상자 출력"
        android:id="@+id/dlgshow"/>

  3) Activity.java 파일의 onCreate 메소드 수정

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dialog_project);

        Button dlgshow = (Button)findViewById(R.id.dlgshow);
        dlgshow.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View view) {
                Dialog dlg = new Dialog(DialogProject.this);
                //대화 상자 생성
                TextView textView = new TextView(DialogProject.this);
                textView.setText("대화상자 출력");
                //모양 설정
                dlg.setContentView(textView);
                dlg.setTitle("대화상자");
                //출력
                dlg.show();
            }
        });
    }

 

3. AlertDialog

    => Dialog 클래스는 화면 모든 영역을 직접 디자인, AlertDialog는 문자열 메시지, 타이틀바, 아이콘 영역을 미리 만들어두고 사용자가 선

        택하여 설정시 생성할 수 있도록 만든 Dialog의 고수준 매핑클래스

    => 생성자는 portected로 되어 있어 직접 호출하지 못하고 Builder를 통해서 생성

    => 설정과 관련된 메소드들은 다시AlertDialog를 리턴하기 때문에 메소드를 연속해서 호출하는 형태로 디자인

        - AlertDialog.Builder setMessage(CharSequence message)

        - AlertDialog.Builder setTitle(CharSequence title)

        - AlertDialog.Builder setIcon(int iconId)

        - 이와 유사한 형태의 클래스로는 String이 있음

    => 출력은 Show() 메소드를 이용

    => 어떤 클래스 디자인시, 생성부가 복잡하면 생성자를 private, protect로 숨기고 별도 메소드를 이용하여 생성하도록 만들어 주어야 함

    => 이러한 디자인 패턴을 팩토리 메소드 패턴이라고 함

    => 한 클래스에 설정해야할 내용이 많은 경우 하나의 메소드에서 전부 설정시, 메소드의 모양이 너무 복잡 or 오버로딩 증가 or  별도 클

       래스를 만들어야 하는 일이 발생

        - 위 경우 Map을 이용해서 설정하는 경우가 있는데 바람직하지 않음

        - Map 은 리턴 타입으로 사용하는 것이 좋음

  1) 레이아웃에 버튼을 추가

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="AlertDialog"
        android:id="@+id/alertdlgshow"/>

 

  2) Activity.java 클래스의 onCreate 메소드에 버튼의 클릭이벤트를 작성하여 AlertDialog를 출력

        Button alertdlgshow = (Button)findViewById(R.id.alertdlgshow);
        alertdlgshow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                AlertDialog.Builder dlg = new AlertDialog.Builder(DialogProject.this);
                //메소드 체이닝(연쇄적으로 메소드 호출 - 메소드 수행 종료후 호출하므로 메모리 소모가 증가되지 않음)
                dlg.setTitle("제목")
                        .setMessage("내용")
                        .setIcon(android.R.drawable.ic_dialog_alert)
                        .show();
            }
        });

 

 

    => 연속적으로 메소드를 호출하는 것을 Method Chaining이라고 함

        - 메소드 체이닝을 제작하는 경우 : 작업을 수행시, 설정 할 내용이 많고, 내용이 선택적인 경우

          -> 초기 설정시, 순서, 종류들을 전부 기억해야 하므로 체이닝 형태로 설정할 수 있도록 만들어 줌

    => 대화상자에 버튼 배치

        - 대화상자에는 사용자의 선택을 알수 있도록 버튼 추가 가능

        - AlertDialog에는 버튼을 추가할 수 있는 메소드가 3가지 있는데, 이름만 다르고 사용방법은 모두 동일

 

        - 코딩의 의미를 전달하기 위해 메소드명을 다르게 제작

        - setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) 

        - setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener) 

        - setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) 

        - 여기서 만들어지는 버튼들은 기본적으로 대화상자를 닫는 기능을 가짐

    => 안드로이드는 Back Button을 가지고 있어 BackButton 제작시 대화상자가 닫힘

        - setCancelable(booldean cancelable)을 호출하여 false 대입시 BackButton을 눌러도 대화상자 유지

 

4. 화면 출력 처리 방식

  1) 1초에 1씩 증가하며 텍스트뷰에 숫자를 출력

    => 실행가능한 Activity 생성

    => 레이아웃에 텍스트뷰 1개, 버튼 1개를 배치

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".DisplayActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="value"
        android:id="@+id/tvDisplay"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="대화상자 출력"
        android:id="@+id/btn"/>

</LinearLayout>

 

  2) Activity.java 클래스에 작업을 위한 코드를 작성

public class DisplayActivity extends AppCompatActivity {
    String msg = "기본";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_display);

        final TextView display = (TextView)findViewById(R.id.tvDisplay);
//        try{
//            //1초마다 출력될 것처럼 보이나 실제로는 보아서 출력하므로 "10"만 보임
//            for(int i=1; i<=10; i+=1){
//                Thread.sleep(1000);
//                display.setText("i=" + i);
//            }
//        }catch (Exception e){
//
//        }

        Button btn = (Button)findViewById(R.id.btn);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                AlertDialog.Builder dlg = new AlertDialog.Builder(DisplayActivity.this);
                dlg.setTitle("대화상자 콜백")
                        .setMessage("대화상자는 어떻게 동작?")
                        .setPositiveButton("확인", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                msg = "확인";
                                try{
                                    for(int j=0; j<10; j+=1){
                                        Thread.sleep(1000);
                                        Log.e("j", j+"");
                                    }
                                }catch (Exception e){}
                            }
                        })
                        .setNegativeButton("취소", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                msg = "취소";
                            }
                        })
                        .show();

                        //대화상자가 출력됨과 동시에 수행
                        //대화상자가 닫히고 난 후 수행하는 코드를 만들려면 대화상자의 콜백 메서드를 사용하야 함
                        display.setText(msg);
            }
        });
    }
}

 

**GUI 프로그래밍에서의 화면 출력

    => 하나의 메소드 안에 화면 출력하는 코드와 그렇지 않은 코드가 같이 존재하면 화면 출력하는 코드는 오랜시간이 걸리므로 나중에 처리

        - 화면 출력 이후 작업을 수행하려는 경우 화면 출력 종료후 수행하기 위해 콜백 메소드에 작성해주어야 함

    => CallBack Mathod: 이벤트 발생시 호출되는 메소드

    => 스레드를 이용하지 않고 주기적으로 화면갱신 수행시 모아서 한꺼번에 처리

        - 화면 갱신은 작업시간이 오래걸려 한꺼번에 처리

        - GUI 프로그래밍 시 스레드를 만드는 것을 할 수 있어야 하고, main 스레드와 다른 스레드를 구분할 수 있어야 함

        - main 스레드에서만 화면 갱신 가능하므로 main 스레드에게 수행할 내용을 전달하는 방식을 반드시 알아야 함

 

**목록 선택 대화상자

    => AlertDialog는 버튼을 배치하는 메소드를 3개 가지고 있음

        - Positive, Neutral, Negative

        - 하나의 메소드를 2번 이상 호출하면 마지막에 호출한 것 하나만 유효

        - 이러한 이유로 버튼은 3개까지만 배치가 가능

    => 4개 이상의 목록에서 선택하는 대화상자를 만들고자 할 경우 목록 선택 대화상자를 만들어야 함

        - 이때는 setItems(CharSequence[] items 또는 int itemsId, DialogInterface.OnClickListener listener)를 호출 해야 함

    => 문자열 배열을 리소스로 만들 때는 values 디렉토리에 arrays.xml 파일에 만들면 됨

    => listener의 onClick 메소드의 2번째 매개변수가 선택한 아이템의 인덱스

    => setItems 대신에 setSingleChoiceItems를 호출하면 목록 옆에 라디오 버튼이 생생되어 하나만 선택할 수 있게 해줌

        - 이 메소드는 문자열 배열, 리소스 아이디, ListAdapter, Cursor를 매개변수로 받을 수 있음

    => setMultiChoiceItems 메소드를 호출하면 다중 선택이 가능한 목록 대화상자를 만들 수 있음

        - 이 메소드는 Boolean 배열을 매개변수로 받아 선택한 목록들의 인덱스에 해당하는 데이터만 true로 설정

 

**ProgressDialog

    => 진행상황을 표시해주는 다이얼로그

    => setProgressStyle이라는 메소드를 이용해서 막대모양이나 원 모양의 진행율을 표시할 수 있음

 

**Date or TimePickerDialog

    => DatePickerDialog : 날짜를 선택할 수 있는 대화상자

    => TimePickerDialog : 시간을 선택할 수 있는 대화상자

    => 스마트 폰은 초단위 설정이 안됨

 

**Custom Dialog

    => 사용자가 디자인한 뷰를 출력하는 다이얼로그

    =>  뷰는 자바코드로 직전 생성해서 설정해도 되지만 layout.xml 파일로 만들어서 작성한 후 전개하여 사용 가능

        - 전개 : layout.xml파일로 만든 것을 View 클래스의 객체로 변환하는 작업

1. 로그인 다이얼로그를 만들어서 출력하고 입력한 내용을 확인하는 예제

  1) 실행가능한 Activity를 추가(LoginActivity)

 

  2) Activity_login.xml 파일 수정

    => TextView 1개와 대화상자를 출력할 버튼 1개를 추가

    => 실제 작업이라면 로그인을 할 수 있도록 할 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".LoginActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="\결과"
        android:id="@+id/result"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="로그인"
        android:id="@+id/login"/>
</LinearLayout>

 

  3) res/layout 디렉토리에 로그인 화면으로 사용할 layout파일을 추가하고 작성

    => login.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="5dp">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="아이디"
        android:textSize="20dp"
        android:textAlignment="center"
        android:layout_marginBottom="30dp"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editID"
        android:textAlignment="center"
        android:hint="ID 입력"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/editPW"
        android:hint="PassWord 입력"
        android:textAlignment="center"/>
</LinearLayout>

 

  4) LoginActivity.java 파일에 인스턴스 변수를 선언

    => 버튼과 텍스트 뷰 변수

//화면에 보여지는 버튼과 텍스트 뷰에 대한 변수
Button loginbtn;
TextView result;

 

  5) LoginActivity.java 파일의 onCreate 메소드에 작업 내용을 작성

public class LoginActivity extends AppCompatActivity {
    //화면에 보여지는 버튼과 텍스트 뷰에 대한 변
    Button loginbtn;
    TextView result;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        //뷰 찾아오기
        result = (TextView)findViewById(R.id.result);
        loginbtn = (Button)findViewById(R.id.login);

        //버튼을 눌렀을 때 처리
        loginbtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //layout 파일에 만든 뷰를 전개
                final LinearLayout linear = (LinearLayout)View.inflate(LoginActivity.this, R.layout.login, null);
                //디자인한 뷰를 출력하는 대화상자
                new AlertDialog.Builder(LoginActivity.this)
                        .setTitle("로그인")
                        .setView(linear)
                        .setCancelable(false)
                        .setNegativeButton("취소", null)
                        .setPositiveButton("확인", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                //anonymous class나 lambda식에서는 자신이 속한 메소드의 지역변수 사용 불가
                                //사용하고자 하는 변수가 있으면 이 변수를 인스턴스 변수로 만들거나 final 변수로 만들어야 함
                                //layout파일에 만든 뷰를 전개하

                                //뷰의 참조를 가져올 경우 부모뷰에서 호출해야 함
                                //id를 입력하는 EditText는 linear안에 있으므로 linear가 호출해야 함
                                EditText id = (EditText) linear.findViewById(R.id.editID);
                                EditText password = (EditText)linear.findViewById(R.id.editPW);

                                //입력한 내용을 가지고 로그인 시도를 함
                                //EditText나 TextView에서 가져온 문자열은 자료형이 String이 아니므로 ToString()을 호출하여 문자열로 변환
                                result.setText("아이디 : " + id.getText().toString() + " 비밀번호 : " + password.getText().toString());
                            }
                        })
                        .show();
            }
        });
    }
}

 

**EventHandling

    => Event : 시스템 또는 사용자가 발생시키는 사건

        - 시스템이 발생시키는 사건(Notification)은 사용자에게 주는 알림의 성격이 강하기 때문에 별도로 부르기도 함

    => EventHandler :  Event가 발생하면 호출되는 객체 또는 함수

1. Event 처리 방식

  1) Hierachy Event Model : 클래스에 만들어져 있는 메소드를 재정의 하는 구조

    => 상속받은 클래스에서만 사용 가능

    => 이형태의 이벤트 처리 메소드는 이벤트가 발생한 객체를 매개변수로 갖지 않음

        - 이 메소드가 구현된 클래스의 객체가 이벤트가 발생한 곳이므로 this이면 이벤트가 발생한 객체

    => 이벤트에 대한 정보를 가지고 있는 객체만 매개변수로 만들어짐

  2) Delegation Model : 이벤트를 처리할 객체나 메소드를 지정하는 구조

    => 상속과 상관없이 자신이 처리할 수도 있고 다른 객체에게 위임할 수도 있음

    => 이렇게 이벤트 처리를 위임받은 객체를 Listener라고 함

  3) VC#, MFC, Java의 Swing, Java F/X, iOS, MacOSX등의 모든 GUI프로그래밍 에서 동일

    - 첫번째 매개변수가 이벤트가 발생한 객체냐, 아니면 이벤트가 발생한 객체에 대한 정보를 가진 객체냐의 차이

 

**Hierarchy Event Model

    => 상위 클래스가 가진 이벤트 처리 메소드를 재정의 하는 방식

    => View와 Activity 클래스에는 기본적인 이벤트 처리를 위한 메소드가 내장

        - boolean onTouchEvent(MotionEvent event) :  터치했을 때 호출되는 메소드

        - boolean keyDown(int keyCode, KeyEvent event) : 키보드 눌렀을 때

        - boolean keyUp(int keyCode, KeyEvent event) : 키보드에서 뗏을 때

        - boolean keyLongPress(int keyCode, KeyEvent event) : 길게 눌렀을 때

        - void onBackPressed() : back버튼 눌렀을 때

        - boolean onTrackballEvent(MotionEvent event) : 트랙볼 이벤트

    => boolean을 리턴하는 메소드들은 이벤트 발생시 시스템이 무엇인가를 수행하는 이벤트

        - 리턴하는 값이 true이면 시스템이 가진 처리를 하는 것, false이면 처리하지 않는 것

 

Tip!

1. 제너릭

    => 같은 알고리즘이라면 public void swap(int a, int b) -> public void swap<int>(T a, T b) 가 편리

        - 여러 메소드를 작성할 필요가 없음