[java] 메서드의 호출과 데이터 이동,<>는 자바의 Generics , new ArrayList<>(), 자손과 조상의 interface
메서드의 호출과의 데이터 이동
먼저 public static void solution(int[] duable, List<Dog> dogs) 메서드에 대해 설명드리겠습니다.
java
public static void solution(int[] duable, List<Dog> dogs) {
int [] stone = duable.clone();
List <String> able_members = dogs.stream()
.filter(dog -> canstart(stone, dog))
.map(dog -> dog.name)
.collect(Collectors.toList());
System.out.println("생존자" + able_members);
}
이 메서드의 매개변수는 두 가지입니다.
int[] duable: 이 배열은 돌다리의 각 돌이 견딜 수 있는 무게를 나타냅니다.
List<Dog> dogs: 이 리스트는 강아지들의 정보를 담고 있습니다. 각각의 Dog 객체는 강아지의 이름, 나이, 점프력, 무게 정보를 가지고 있습니다.
메서드 내부에서는 먼저 duable 배열을 clone() 메서드를 사용해 복사하여 stone 배열에 저장합니다. 이후 강아지들이 돌다리를 건널 수 있는지 체크합니다. 각강아지들이 돌다리를 건너는 시뮬레이션은 canstart(stone, dog) 메서드에서 이루어집니다.
java
public static boolean canstart(int[] stone, Dog dog) {
int jumpstart = dog.jump -1;
for (int j = jumpstart; j <stone.length; j+= dog.jump){
stone[j] -= dog.weight;
if (stone[j] <0)
return false;
}
return true;
}
canstart 메서드는 강아지 한 마리(Dog dog)가 주어진 돌다리(int[] stone)를 건널 수 있는지를 판단합니다. 강아지는 자신의 점프력(dog.jump)만큼 돌다리를 건너뛰며, 돌다리 위의 돌은 강아지의 무게(dog.weight)만큼의 무게를 견뎌야 합니다. 만약 어떤 돌이 강아지의 무게를 견디지 못한다면 (stone[j] < 0), 그 강아지는 돌다리를 건너는 데 실패하고, canstart 메서드는 false를 반환합니다. 만약 강아지가 무사히 돌다리를 건넜다면, canstart 메서드는 true를 반환합니다.
이렇게 각 강아지가 돌다리를 건널 수 있는지를 체크한 후, solution 메서드에서는 건널 수 있는 강아지들의 이름을 리스트에 담아 출력합니다.
그림으로 나타내면 아래와 같습니다.
- solution 메서드 호출
- 매개변수: duable=[1, 2, 1, 4], dogs=[Dog("루비독", "95년생", 3, 4), Dog("피치독", "95년생", 3, 3), Dog("씨-독", "72년생", 2, 1), Dog("코볼독", "59년생", 1, 1)]
- stone 배열 생성: stone=[1, 2, 1, 4]
- 강아지 별로 canstart 메서드 호출
- Dog("루비독", "95년생", 3, 4) 호출
- 3칸씩 점프 -> 무게 4 감소 -> 돌다리 견딜 수 없음 -> false 반환
- Dog("피치독", "95년생", 3, 3) 호출
- 3칸씩 점프 -> 무게 3 감소 -> 돌다리 견딜 수 있음 -> true 반환
- Dog("씨-독", "72년생", 2, 1) 호출
- 2칸씩 점프 -> 무게 1 감소 -> 돌다리 견딜 수 있음 -> true 반환
- Dog("코볼독", "59년생", 1, 1) 호출
- 1칸씩 점프 -> 무게 1 감소 -> 돌다리 견딜 수 있음 -> true 반환
- 생존자 출력: ["피치독", "씨-독", "코볼독"]
<>는 자바의 Generics를 나타냅니다. Generics는 클래스 내부에서 사용할 데이터 타입을 외부에서 지정할 수 있게 하는 기법입니다.
<T>는 자바의 제네릭(Generic)을 나타냅니다.
제네릭은 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시점에 미리 지정해주는 방법입니다.
이를 통해 타입 안정성을 높이고, 타입 변환에 따른 불필요한 코드를 줄일 수 있습니다.
아래는 제네릭을 사용하는 자바 코드 예시입니다.
java
// 제네릭을 사용하는 클래스 정의
public class Box<T> {
private T object;
public void set(T object) {
this.object = object;
}
public T get() {
return object;
}
}
Box<T> 클래스는 제네릭을 사용하여 내부에서 다룰 데이터 타입을 외부에서 지정할 수 있게 합니다.
T는 타입 파라미터로, 실제 타입은 인스턴스를 생성할 때 결정됩니다.
이제 Box 클래스의 인스턴스를 생성하면서 타입 파라미터를 지정해보겠습니다.
java
public class Main {
public static void main(String[] args) {
Box<Integer> integerBox = new Box<>(); // Integer 타입으로 지정
integerBox.set(123);
System.out.println(integerBox.get());
Box<String> stringBox = new Box<>(); // String 타입으로 지정
stringBox.set("Hello, World!");
System.out.println(stringBox.get());
}
}
이 코드를 실행하면,
sh
123
Hello, World!
이라는 출력을 볼 수 있습니다.
integerBox는 Box<Integer> 타입으로, 내부에서 다루는 데이터 타입을 Integer로 지정하였습니다.
stringBox는 Box<String> 타입으로, 내부에서 다루는 데이터 타입을 String으로 지정하였습니다.
이렇게 제네릭을 사용하면, 클래스나 메소드에서 다루는 데이터 타입을 미리 지정할 수 있으므로 코드의 재사용성을 높이고, 타입 안정성을 보장할 수 있습니다.
제 List<Dog> dogs = new ArrayList<>();, 에서 new ArrayList<>(); 부분에서 new 는 객체를 생성해서 힙에 올릴려고 하는건 알겟어 그렇다면 <> 이부분은 앞에 <Dog> 타입이 적용되기 위해 쓰는건지?에 대한것 하나와 (); 이부분은 왜 사용이 되고잇지?
new ArrayList<>();에서 new는 말씀하신 것처럼 새로운 ArrayList 객체를 생성하는 것을 의미합니다.
<>는 자바의 Generics를 나타냅니다. Generics는 클래스 내부에서 사용할 데이터 타입을 외부에서 지정할 수 있게 하는 기법입니다. 여기서 <Dog>는 ArrayList가 담을 객체의 타입을 Dog로 지정하겠다는 의미입니다.
즉, new ArrayList<Dog>();는 Dog 객체만을 담을 수 있는 새로운 ArrayList를 생성하겠다는 의미입니다.
하지만 자바 7부터는 우변의 타입 인자를 생략하고 <> (다이아몬드 연산자)만 쓸 수 있게 되었습니다.
컴파일러가 알아서 왼쪽의 타입 인자를 참조합니다. 따라서 new ArrayList<>();는 new ArrayList<Dog>();와 동일한 의미입니다.
마지막으로, ()는 ArrayList의 생성자를 호출하는 것을 의미합니다.
new ArrayList<>();는 ArrayList의 기본 생성자를 호출해서 새로운 ArrayList 객체를 생성하고 초기화하는 것을 의미합니다.
이때 생성자의 괄호 안에 특정 값을 넣으면, 그 값으로 초기화를 수행하는 생성자를 호출하게 됩니다. 여기서는 아무런 값을 넣지 않았으므로, 기본 생성자를 호출하는 것입니다.
new ArrayList<>(); 에서 기본 생성자를 호출한다?????
new ArrayList<>(); 코드는 ArrayList의 기본 생성자를 호출하는 것입니다.
이렇게 하면 비어 있는 ArrayList 인스턴스가 생성됩니다. 이 인스턴스는 초기 용량(capacity)을 가지지만,
아직 아무런 요소(element)도 포함하고 있지 않습니다.
다음은 이를 설명하는 자바 코드 예시입니다.
java
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
// ArrayList의 기본 생성자를 호출하여 빈 리스트 생성
ArrayList<String> list = new ArrayList<>();
System.out.println(list.isEmpty()); // 리스트가 비어 있는지 확인
// 리스트에 요소 추가
list.add("Apple");
list.add("Banana");
list.add("Cherry");
System.out.println(list); // 리스트 출력
}
}
이 코드를 실행하면,
sh
true
[Apple, Banana, Cherry]
이라는 출력을 볼 수 있습니다.
처음에 list.isEmpty()를 호출하면 true가 출력되어 리스트가 비어 있음을 확인할 수 있습니다.
그 후 list.add() 메서드를 사용하여 리스트에 요소를 추가하고, 리스트를 출력하면 추가된 요소들을 확인할 수 있습니다.
이렇게 ArrayList의 기본 생성자를 호출하면,
필요에 따라 크기가 자동으로 조정되는 동적 배열(dynamic array)를 사용할 수 있습니다.
이는 고정 크기의 배열보다 더 유연하게 데이터를 다룰 수 있게 해줍니다.
Interface 란 다중상속을 한다???
인터페이스는 구현 클래스의 한 종류의 '부모'라고 볼 수 있습니다. 클래스는 상속을 통해 부모 클래스의 속성과 메서드를 물려받는 반면, 인터페이스는 클래스에 특정 메서드를 구현하도록 강제하는 역할을 합니다.
다음은 extends와 implements 키워드를 사용하여 클래스와 인터페이스의 관계를 나타내는 자바 코드 예시입니다.
java
// 부모 클래스 정의
public class Unit {
public void move() {
System.out.println("Unit is moving.");
}
}
// 인터페이스 정의
public interface Fightable {
void attack();
}
// 부모 클래스를 상속받고, 인터페이스를 구현한 클래스 정의
public class Warrior extends Unit implements Fightable {
@Override
public void attack() {
System.out.println("Warrior is attacking.");
}
}
Unit은 부모 클래스로서 move() 메서드를 가지고 있습니다. Fightable은 인터페이스로서 attack() 메서드를 정의하고 있습니다. Warrior 클래스는 Unit 클래스를 상속받아 move() 메서드를 사용할 수 있고, Fightable 인터페이스를 구현하여 attack() 메서드를 오버라이드하였습니다.
이제 Warrior 클래스의 객체를 생성하여 해당 메서드들을 호출해 보겠습니다.
java
public class Main {
public static void main(String[] args) {
Warrior myWarrior = new Warrior();
myWarrior.move();
myWarrior.attack();
}
}
이 코드를 실행하면,
sh
Unit is moving.
Warrior is attacking.
이라는 출력을 볼 수 있습니다.
따라서, Warrior 클래스는 Unit 클래스의 자식이자 Fightable 인터페이스의 구현체입니다.
이렇게 클래스와 인터페이스의 관계를 이해하고 활용하는 것은 객체지향 프로그래밍에서 중요한 부분입니다.
Fightable 인터페이스를 구현한 클래스의 인스턴스만 가능하다는 것이다
자손이 조상을 가르킬때는 interface에서
타입을 생략이 가능하다
자바에서는 인터페이스 타입의 참조 변수를 사용하여 구현 클래스의 인스턴스를 가리킬 수 있습니다.
이를 통해 구현 클래스의 내부 구현을 숨기고, 인터페이스를 통해 객체를 사용하는 방식을 캡슐화라고 합니다.
아래는 이를 설명하는 자바 코드 예시입니다.
java
// 인터페이스 정의
public interface Animal {
void eat();
void sleep();
}
// 인터페이스를 구현한 클래스 정의
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("Dog is eating.");
}
@Override
public void sleep() {
System.out.println("Dog is sleeping.");
}
public void bark() {
System.out.println("Dog is barking.");
}
}
Animal은 인터페이스로, 'eat'와 'sleep'라는 메서드를 정의하고 있습니다. 이 인터페이스를 Dog라는 클래스가 구현하고 있습니다. Dog 클래스는 Animal 인터페이스에서 정의된 메서드들을 모두 구현해야 합니다. 또한 Dog 클래스는 'bark'라는 자신만의 메서드를 가지고 있습니다.
이제 Animal 타입의 참조 변수를 사용하여 Dog 클래스의 인스턴스를 가리킬 수 있습니다.
java
public class Main {
public static void main(String[] args) {
Animal myPet = new Dog(); // 인터페이스 타입의 참조 변수를 사용
myPet.eat();
myPet.sleep();
// myPet.bark(); // 컴파일 오류
}
}
이 코드를 실행하면,
sh
Dog is eating.
Dog is sleeping.
이라는 출력을 볼 수 있습니다
myPet.bark()를 호출하려고 하면 컴파일 오류가 발생합니다.
이는 myPet이 Animal 타입의 참조 변수이기 때문에, Animal 인터페이스에 정의된 메서드만 호출할 수 있기 때문입니다.
이를 통해 Dog 클래스의 내부 구현을 숨기고, Animal 인터페이스를 통해 객체를 사용하는 캡슐화를 구현할 수 있습니다. 이렇게 하면 코드의 유지보수가 용이해집니다.
'컴퓨터공부 > Java' 카테고리의 다른 글
[java 자바공부] 스트림과 람다 인스턴스 메소드, 정적 메소드, 동적 메소드, 코딩테스트 공부 (1) | 2023.12.21 |
---|---|
[java 공부] 제어자, 추상메서드, abstract , static (0) | 2023.11.07 |
[java공부] 인터페이스와 추상메서드의 개념 (0) | 2023.11.07 |
[java] instanceof 연산자, 형변환이 가능한것이냐 불가능을 체크하고 객체를 만들어야되는 코드, 추상메서드 (abstract method), (0) | 2023.11.06 |
댓글