Ref_인터페이스 / 추상클래스 / 중첩 / 키워드

Dev.Hammy·2023년 12월 28일
0

Spring Guides

목록 보기
38/46

interface / abstract class

측면인터페이스추상 클래스
선언 방식interface 키워드로 선언abstract class 키워드로 선언
다중 상속다중 상속 지원 (extends 키워드를 통해 여러 인터페이스 상속 가능)단일 상속만 지원 (extends 키워드를 통해 하나의 클래스만 상속 가능)
메서드 구현모든 메서드는 추상 메서드로 기본적으로 선언됨추상 메서드, 일반 메서드, final, private 등 다양한 접근자 지원
인스턴스화 가능 여부직접적으로 인스턴스화할 수 없음 (객체 생성 불가)직접적으로 인스턴스화 불가하지만, 하위 클래스로 상속받아 인스턴스화 가능
구현체에서의 구현모든 메서드는 구현체에서 반드시 구현되어야 함추상 메서드는 반드시 하위 클래스에서 오버라이드해야 하지만, 나머지 메서드는 선택적으로 오버라이드 가능
유틸리티 기능메서드 시그니처를 정의하여 구현체 간에 일관성을 제공내부적인 동작 로직이나 유틸리티 기능을 제공할 수 있음

추상클래스는 인스턴스 변수, 즉 필드를 가질 수 있지만, 인터페이스는 abstract, static, default 메서드와 static final 형태의 상수만을 가질 수 있습니다.

래퍼 클래스 패턴은 기존 클래스를 상속받거나 변경하지 않고, 새로운 기능을 추가하기 위한 디자인 패턴입니다. 이는 인터페이스를 이용하여 기능을 추가하고, 이를 상속받는 클래스가 해당 기능을 사용하게 하는 것을 의미합니다.

java 8 : static, default 메서드 도입

default

  1. 역방향 호환성(Backward Compatibility):

    • 기존에 작성된 코드가 새로운 버전이나 변경된 환경에서도 여전히 동작하는 데 있어 중요한 개념입니다.
    • 코드가 업데이트되거나 새로운 기능이 추가되더라도 기존 코드의 수정 없이 여전히 정상적으로 동작해야 합니다.
    • 이는 기존의 소프트웨어와 새로운 버전 간의 호환성을 유지하고 기존 사용자들이 새로운 버전을 사용할 때 발생할 수 있는 문제를 방지하는 데 도움이 됩니다.
  2. 인터페이스의 확장성(Interface Evolution):

    • 이는 인터페이스의 변경과 관련된 개념으로, 기존의 인터페이스를 확장하거나 변경함으로써 새로운 기능을 추가할 수 있는 능력을 의미합니다.
    • default 메서드와 같은 기능은 이전에 만들어진 인터페이스에 새로운 메서드를 추가하면서도 이미 구현된 클래스에 영향을 미치지 않고 새로운 기능을 도입할 수 있도록 합니다.
    • 기존에 없던 메서드를 추가하거나 새로운 기능을 제공함으로써 인터페이스의 확장성을 높일 수 있습니다.

static

유틸리티 메서드 구성(Utility Method Composition):

static 메서드를 인터페이스에 추가할 수 있게 되면, 해당 인터페이스를 사용하여 유틸리티 메서드를 그룹화할 수 있습니다. 이렇게 하면 특정 기능을 위한 정적 메서드들을 해당 인터페이스로 묶어 관리할 수 있습니다. Java에서 인터페이스에 정의된 static 메서드는 상속되거나 오버라이드될 수 없습니다. 또한 구현 클래스에서 반드시 호출할 필요는 없습니다.

인터페이스를 import / implement

인터페이스를 import하는 것과 implement하는 것은 Java에서 서로 다른 개념입니다.

  1. 인터페이스를 import하는 것:

    • import 문은 Java에서 다른 패키지에 있는 클래스나 인터페이스를 현재 파일에서 사용하기 위해 선언하는 것입니다.
    • import를 사용하여 패키지에 있는 클래스나 인터페이스의 이름을 짧게 사용할 수 있게 해줍니다. 예를 들어, import java.util.List;를 선언하면 List라고만 써도 java.util 패키지 내의 List를 사용할 수 있습니다.
  2. 인터페이스를 implement(구현)하는 것:

    • 클래스가 인터페이스를 구현하기 위해서는 implements 키워드를 사용합니다. 이는 해당 클래스가 특정 인터페이스의 메서드들을 구현하겠다는 것을 의미합니다.
    • 인터페이스를 구현하는 클래스는 인터페이스의 모든 메서드(static, default 메서드 제외)를 반드시 구현해야 합니다. 그렇지 않으면 컴파일 에러가 발생합니다.

중첩 interface

중첩 인터페이스는 하나의 인터페이스 안에 또 다른 인터페이스가 정의된 것을 말합니다. 이는 코드의 구조를 조직화하고 관련 있는 부분을 묶어서 관리할 수 있게 해줍니다.

중첩 인터페이스는 외부 인터페이스의 멤버로서 동작하며, 외부 클래스나 인터페이스의 이름을 접근할 때는 OuterInterface.InnerInterface와 같이 점으로 구분하여 사용합니다.

중첩 인터페이스는 외부 클래스의 메소드들과 마찬가지로 구현되어 사용될 수 있습니다.

CoffeeConfiguration 클래스

import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class CoffeeConfiguration {


//... 생략

RedisSerializationContext.RedisSerializationContextBuilder<String, Coffee> builder = 
RedisSerializationContext.newSerializationContext(new StringRedisSerializer());

RedisSerializationContext<String, Coffee> context = builder.value(serializer).build();

//... 중략

RedisSerializationContext 인터페이스

public interface RedisSerializationContext<K, V> {
    static <K, V> RedisSerializationContextBuilder<K, V> newSerializationContext() {
        return new DefaultRedisSerializationContext.DefaultRedisSerializationContextBuilder();
    }
    
//... 중략

    public interface SerializationPair<T> {
        static <T> SerializationPair<T> fromSerializer(RedisSerializer<T> serializer) {
            Assert.notNull(serializer, "RedisSerializer must not be null");
            return new RedisSerializerToSerializationPairAdapter(serializer);
        }

//... 중략

    public interface RedisSerializationContextBuilder<K, V> {
    
    //... 중략
    
    	RedisSerializationContextBuilder<K, V> value(SerializationPair<V> pair);
        
    //... 중략
    
    	RedisSerializationContext<K, V> build();
	}
}

클래스 CoffeeConfiguration에서는 RedisSerializationContext 인터페이스를 구현(implement)하지 않고, 해당 인터페이스의 구성 요소 중 일부를 사용하기 위해 import를 통해 가져왔습니다. 이 클래스에서는 RedisSerializationContextBuilder를 사용하기 위해 RedisSerializationContext를 통해 접근하여 builder 변수에 연결했습니다.

newSerializationContext() 메서드는 RedisSerializationContextBuilder의 외부에 선언된 static 메서드이므로 RedisSerializationContext에서 직접 호출할 수 있습니다.

그러나 value()build()RedisSerializationContextBuilder의 내부에 선언된 abstract 메서드이므로, 점 접근법(dot notation)을 사용하여 RedisSerializationContext.RedisSerializationContextBuilder<K, V> 타입을 가진 builder를 통해 접근해야 합니다.

클래스 리터럴과 클래스 시그니처

  • "클래스 리터럴" 이라는 용어는 실제로 해당 클래스의 메타데이터를 나타내는 것으로, Java에서 Class<T> 타입의 객체입니다. 클래스의 리터럴은 클래스의 메타정보를 포함하고 있어서, 시그니처에 대한 정보를 담고 있는 것으로 볼 수 있습니다. 시그니처는 해당 클래스의 구조와 관련된 정보를 포함하는데, 클래스의 리터럴을 통해 이 정보에 접근할 수 있습니다.
public class ClassLiteralExample {
    public static void main(String[] args) {
        Class<String> stringClass = String.class;
        System.out.println("stringClass: " + stringClass); // stringClass 출력
        System.out.println("String.class: " + String.class); // String.class 출력
    }
}

String.class의 결과로 반환되는 것은 Class<String> 객체입니다. 이것은 String 클래스의 메타정보를 담고 있는 객체이며, 해당 클래스의 구조, 메서드, 필드 등에 대한 정보를 담고 있습니다. stringClass라는 변수가 이 객체를 가리키고 있습니다.

출력 결과는 아래와 같습니다.

stringClass: class java.lang.String
String.class: class java.lang.String
  • 반면에 "시그니처" 는 일반적으로 클래스, 메서드 또는 필드의 형태와 관련된 정보를 의미합니다. 이것은 해당 항목의 타입, 매개변수, 반환형 등을 설명하는 것을 말합니다.

매개변수에서 클래스와 클래스 리터럴의 사용

클래스 리터럴을 받는 경우에는 클래스의 메타데이터를 다루거나, 타입 안전성을 보장하기 위해 사용되고, 클래스 자체를 받는 경우에는 클래스의 객체를 다루거나, 다형성을 활용하는 등의 목적으로 활용될 수 있습니다.

클래스 리터럴을 매개변수로

팩토리 메서드 패턴

public class FactoryExample {
    public static <T> T createInstance(Class<T> clazz) throws IllegalAccessException, InstantiationException {
        return clazz.newInstance(); // 클래스로부터 인스턴스를 생성하는 간단한 팩토리 메서드
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        // 클래스를 매개변수로 전달하여 객체 생성
        String instance = FactoryExample.createInstance(String.class);
        System.out.println(instance); // 출력: null (빈 문자열)
    }
}

타입 안정성 유틸리티

public class TypeUtilityExample {
    static <T> void printClassName(Class<T> clazz) {
        System.out.println("클래스 이름: " + clazz.getName());
    }

    public static void main(String[] args) {
        // 클래스를 매개변수로 받아서 클래스 이름을 출력하는 유틸리티 예시
        TypeUtilityExample.printClassName(String.class); // 출력: "클래스 이름: java.lang.String"
    }
}

클래스 자체를 매개변수로

스트래티지 패턴

// 전략(스트래티지) 인터페이스
interface Strategy {
    void execute();
}

// 전략을 구현한 클래스
class StrategyA implements Strategy {
    public void execute() {
        System.out.println("전략 A 수행");
    }
}

class StrategyB implements Strategy {
    public void execute() {
        System.out.println("전략 B 수행");
    }
}

// 전략을 사용하는 클래스
class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeStrategy() {
        strategy.execute();
    }
}

public class StrategyPatternExample {
    public static void main(String[] args) {
        // 전략을 클래스로 받아 객체 생성
        Context contextA = new Context(new StrategyA());
        Context contextB = new Context(new StrategyB());

        contextA.executeStrategy(); // 출력: "전략 A 수행"
        contextB.executeStrategy(); // 출력: "전략 B 수행"
    }
}

상속과 다형성

class Animal {
    void makeSound() {
        System.out.println("동물 소리");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("멍멍");
    }
}

public class PolymorphismExample {
    static void soundOfAnimal(Animal animal) {
        animal.makeSound();
    }

    public static void main(String[] args) {
        // 상위 클래스를 매개변수로 받아 하위 클래스의 인스턴스를 다룸
        Animal animal1 = new Animal();
        Animal animal2 = new Dog();

        soundOfAnimal(animal1); // 출력: "동물 소리"
        soundOfAnimal(animal2); // 출력: "멍멍"
    }
}

생성자 내에서 this()로 다른 생성자 호출하기

Jackson2JsonRedisSerializer 클래스

public class Jackson2JsonRedisSerializer<T> implements RedisSerializer<T> {

    public static final Charset DEFAULT_CHARSET;
    private final JavaType javaType;
    private ObjectMapper mapper;
    private final JacksonObjectReader reader;
    private final JacksonObjectWriter writer;

    public Jackson2JsonRedisSerializer(Class<T> type) {
        this(new ObjectMapper(), type);
    }
//...중략

    public Jackson2JsonRedisSerializer(ObjectMapper mapper, Class<T> type) {
        Assert.notNull(mapper, "ObjectMapper must not be null");
        Assert.notNull(type, "Java type must not be null");
        this.javaType = this.getJavaType(type);
        this.mapper = mapper;
        this.reader = JacksonObjectReader.create();
        this.writer = JacksonObjectWriter.create();
    }
 //...생략   

this 키워드는 클래스의 인스턴스를 가리키는 것이 맞습니다. 그러나 생성자에서 this()를 호출할 때, 이는 같은 클래스 내의 다른 생성자를 의미합니다.

this(new ObjectMapper(), type)Jackson2JsonRedisSerializer(ObjectMapper mapper, Class<T> type)라는 두 개의 매개변수를 가진 다른 생성자를 호출하려는 의도를 보인 것입니다.

이렇듯 Jackson2JsonRedisSerializer(Class<T> type) 생성자가 호출될 때, 내부적으로 Jackson2JsonRedisSerializer(ObjectMapper mapper, Class<T> type) 생성자를 호출함으로써 코드 중복을 피하도록 합니다.

ObjectMapper 클래스

public class ObjectMapper extends ObjectCodec implements Versioned, Serializable {

//... 중략
    public ObjectMapper() {
        this((JsonFactory)null, (DefaultSerializerProvider)null, (DefaultDeserializationContext)null);
    }
    
    public ObjectMapper(JsonFactory jf) {
        this(jf, (DefaultSerializerProvider)null, (DefaultDeserializationContext)null);
    }
//... 생략    

위 코드에서 보이는 두 개의 생성자는 모두 JsonFactory, DefaultSerializerProvider, DefaultDeserializationContext 타입을 가지는 세 개의 매개변수를 사용하는 ObjectMapper 생성자를 this()로 호출하고 있다.

finalstatic

final 값 할당 시점

final로 선언된 변수는 선언 시점이 아니라 생성자에서 값이 할당되어야 합니다. 그렇지 않으면 컴파일 오류가 발생합니다. 일반적으로 final 필드는 선언과 동시에 또는 생성자에서 값을 한 번만 할당해야 합니다. 그렇지 않으면 컴파일러가 오류를 내거나 경고를 발생시킬 수 있습니다.

하지만 제공하신 코드에서 _recipe_factory는 생성자에서 초기값으로 null을 할당받고 있습니다. 이는 유효한 방법입니다. 생성자 내부에서 final 필드를 한 번만 초기화할 수 있고, 이후에는 해당 값이 변하지 않아야 합니다. 코드에서 보듯이, 생성자에서 한 번만 초기화되고 그 이후에는 변경되지 않는다면 이는 final 필드의 규칙을 지키고 있는 것입니다.

public abstract class SourceOrigin {

    protected static final BaseSource<Object> BASE_UNKNOWN_SOURCE = new Brewer();
    protected final SourceRecipe _recipe;
    protected final SourceFactory _factory;
    protected BaseSource<Object> _unknownSource;

    public SourceOrigin(){
        this._unknownSource = BASE_UNKNOWN_SOURCE;
        this._recipe = null;
        this._factory = null;
    }
}

static final로 선언된 필드인 BASE_UNKNOWN_SOURCE는 해당 클래스에 속한 상수입니다. 이것은 수정될 수 없는 불변의 상태를 갖습니다.

클래스의 상속 관계

https://www.baeldung.com/java-reflection-class-fields

네, 많은 내용을 잘 설명하셨습니다.

  1. 부모 클래스의 non-final, non-static, non-private 속성(인스턴스 속성): 부모 클래스의 인스턴스 생성자를 통해 값을 할당하고, 자식 클래스에서 상속받아 사용하거나 필요에 따라 재정의할 수 있습니다.

  2. 부모 클래스의 non-final, non-private, static 속성(스태틱 속성): 인스턴스나 자식 클래스에서는 직접 접근할 수 없고, 부모 클래스 이름을 통해서 접근하여 값을 수정할 수 있습니다.

  3. 부모 클래스의 final 속성: final 속성은 변경할 수 없는 값으로 취급되며, 자식 클래스, 부모 클래스의 인스턴스, 또는 부모 클래스 이름을 통한 접근을 통해 값을 읽을 수 있지만, 수정은 불가능합니다. final인 속성이 생성자에서 초기화되지 않았다면, 생성자에서 this를 통해 값을 할당할 수 있습니다.

  4. 부모 클래스의 private 속성: private 속성은 클래스 내부에서만 접근 가능하며, 부모 클래스의 인스턴스, 자식 클래스, 또는 부모 클래스 이름을 통한 접근을 통해 접근하거나 수정할 수 없습니다.

자식 클래스에서 부모 클래스의 기본 생성자가 호출

Parent 클래스 생성

public class Parent {
    public Parent (){
        System.out.println("나는 Parent 기본 생성자");
    };
}

Child 클래스가 Parent 클래스 상속

public class Child extends Parent{
    Child(){
        System.out.println("나는 Child 기본 생성자");
    }
}

Child 인스턴스 생성 시 Parent 기본 생성자 자동 실행

public static void main(String[] args) {
        Child child = new Child();
}

출력 결과

나는 Parent 기본 생성자
나는 Child 기본 생성자

추상 클래스의 상속 관계

분리된 추상 클래스를 만드는 이유 중 하나는 개념적으로 관련 있는 일부 기능을 한 곳에 모으는 것입니다. 하나의 추상 클래스가 지나치게 커지거나, 여러 목적을 위한 다양한 초기화를 처리해야 하는 경우에 이를 분리하여 관리하는 것이 가독성과 유지보수성을 높일 수 있습니다.

protected와 super()

DefaultSerializerProvider 추상클래스

public abstract class DefaultSerializerProvider extends SerializerProvider implements Serializable {
    private static final long serialVersionUID = 1L;
    protected transient Map<Object, WritableObjectId> _seenObjectIds;
    protected transient ArrayList<ObjectIdGenerator<?>> _objectIdGenerators;
    protected transient JsonGenerator _generator;

    protected DefaultSerializerProvider() {
    }

    protected DefaultSerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) {
        super(src, config, f);
    }

추상 클래스는 인스턴스화될 수 없기 때문에 자체적인 인스턴스 생성이 불가능합니다. 하지만 추상 클래스 역시 생성자를 가질 수 있습니다.
추상 클래스의 생성자는 protected로 선언되며, 외부에서 직접 호출할 수 없고 하위 클래스 내부에서 super()를 통해 호출됩니다. 하위 클래스에서 반드시 호출해야 하는 초기화 로직이 있을 때, 이러한 목적으로 추상 클래스 내부에 생성자를 구현할 수 있습니다.

SerializerProvider 클래스의 생성자인 protected SerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f)는 자식 클래스에서 사용 가능하도록 protected로 선언되어 있습니다. 자식 클래스에서 부모 클래스의 특정 상태를 초기화하거나 확장할 수 있습니다.

같은 클래스 객체를 매개변수로 사용하는 생성자

SerializerProvider 추상클래스

public abstract class SerializerProvider extends DatabindContext {

//...중략

    protected SerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) {
        this._unknownTypeSerializer = DEFAULT_UNKNOWN_SERIALIZER;
        this._nullValueSerializer = NullSerializer.instance;
        this._nullKeySerializer = DEFAULT_NULL_KEY_SERIALIZER;
        this._serializerFactory = f;
        this._config = config;
        this._serializerCache = src._serializerCache;
        this._unknownTypeSerializer = src._unknownTypeSerializer;
        this._keySerializer = src._keySerializer;
        this._nullValueSerializer = src._nullValueSerializer;
        this._nullKeySerializer = src._nullKeySerializer;
        this._stdNullValueSerializer = this._nullValueSerializer == DEFAULT_NULL_KEY_SERIALIZER;
        this._serializationView = config.getActiveView();
        this._attributes = config.getAttributes();
        this._knownSerializers = this._serializerCache.getReadOnlyLookupMap();
    }
    //... 생략

주어진 생성자 코드에서 매개변수로 받은 SerializerProvider src는 생성자의 매개변수로 전달된 것으로, 해당 생성자 내에서 SerializerProvider 타입의 객체를 새로운 인스턴스로 생성하는 것이 아니라, 이미 생성된 src 객체의 참조를 받고 있습니다.

this는 현재 객체의 참조를 나타내며, 생성자 내부에서 this는 현재 객체를 가리킵니다. SerializerProvider src는 외부에서 전달된 다른 객체를 가리키며, 이를 생성자 내부에서 사용할 수 있게 됩니다.

따라서 thissrc는 서로 다른 객체를 가리키며, 생성자 내부에서 각각의 객체를 참조하는데 사용됩니다. this는 현재 인스턴스를 나타내고, src는 생성자를 호출할 때 전달된 다른 SerializerProvider 객체를 가리킵니다.

생성자를 이용한 인스턴스의 복사

https://blog.naver.com/29java/70187757119

class Car {
    String color;    // 생상
    String gearType;    // 변속기 종류 - auto(자동), manual(수동)    
    int door;    // 문의 개수
    
    Car() {
        this("white", "auto", 4);
    }
    
    Car(Car c) {    // 인스턴스의 복사를 위한 생성자
        color = c.color;
        gearType = c.gearType;
        door = c.door;
    }
    
    Car(String color, String gearType, int door) {
        this.color = color;
        this.gearType = gearType;
        this.door = door;
    }
}
class CarTest3 {
    public static void main(String[] args) {
        Car c1 = new Car();
        Car c2 = new Car(c1); // c1의 복사본 c2를 생성한다.
        // 이하 생략

추상클래스 내부에서 해당 추상클래스를 상속하는 final 클래스

DefaultSerializerProvider

DefaultSerializerProvider.Impl

DefaultSerializerProvider

public abstract class DefaultSerializerProvider extends SerializerProvider implements Serializable {

//... 중략

    protected DefaultSerializerProvider() {
    }

    protected DefaultSerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) {
        super(src, config, f);
    }

    protected DefaultSerializerProvider(DefaultSerializerProvider src) {
        super(src);
    }


    public abstract DefaultSerializerProvider createInstance(SerializationConfig var1, SerializerFactory var2);

    public DefaultSerializerProvider copy() {
        throw new IllegalStateException("DefaultSerializerProvider sub-class not overriding copy()");
    }

//...생략
    

DefaultSerializerProvider.Impl

//...생략
public static final class Impl extends DefaultSerializerProvider {
        private static final long serialVersionUID = 1L;

        public Impl() {
        }

        public Impl(Impl src) {
            super(src);
        }

        protected Impl(SerializerProvider src, SerializationConfig config, SerializerFactory f) {
            super(src, config, f);
        }

        public DefaultSerializerProvider copy() {
            return (DefaultSerializerProvider)(this.getClass() != Impl.class ? super.copy() : new Impl(this));
        }

        public Impl createInstance(SerializationConfig config, SerializerFactory jsf) {
            return new Impl(this, config, jsf);
        }
    }
}

DefaultDeserializationContext

public abstract class DefaultDeserializationContext extends DeserializationContext implements Serializable {
    private static final long serialVersionUID = 1L;
    protected transient LinkedHashMap<ObjectIdGenerator.IdKey, ReadableObjectId> _objectIds;
    private List<ObjectIdResolver> _objectIdResolvers;

    protected DefaultDeserializationContext(DeserializerFactory df, DeserializerCache cache) {
        super(df, cache);
    }

    protected DefaultDeserializationContext(DefaultDeserializationContext src, DeserializationConfig config, JsonParser p, InjectableValues values) {
        super(src, config, p, values);
    }
//... 생략

Serializable 인터페이스

package java.io;

public interface Serializable {
}

Serializable 인터페이스는 마킹 인터페이스입니다. 어떤 클래스가 직렬화될 수 있다는 것을 나타내는데, 메서드를 가지지 않고 단순히 해당 클래스가 직렬화 가능하다는 표시를 합니다. 이를 통해 객체를 파일에 저장하거나 네트워크로 전송할 수 있도록 합니다.

Versioned 인터페이스

package com.fasterxml.jackson.core;

public interface Versioned {
    Version version();
}

Versioned 인터페이스에서 version() 메서드를 통해 Version 클래스의 버전 정보를 반환하는 것은 느슨한 결합을 유지하기 위함일 수 있습니다. 이렇게 함으로써 Version 클래스의 변경이 Versioned 인터페이스의 구현에 영향을 덜 주면서도 버전 정보를 요구하는 부분에서 해당 기능을 사용할 수 있습니다. 또한, Version 클래스를 직접 사용하지 않고 Versioned 인터페이스를 통해 버전 정보에 접근하면, 다른 버전 정보를 제공하는 클래스로 손쉽게 교체할 수 있습니다.

Version 클래스

package com.fasterxml.jackson.core;

import java.io.Serializable;

public class Version implements Comparable<Version>, Serializable {

//... 중략

    public Version(int major, int minor, int patchLevel, String snapshotInfo, String groupId, String artifactId) {
        this._majorVersion = major;
        this._minorVersion = minor;
        this._patchLevel = patchLevel;
        this._snapshotInfo = snapshotInfo;
        this._groupId = groupId == null ? "" : groupId;
        this._artifactId = artifactId == null ? "" : artifactId;
    }
//... 중략    

    public int compareTo(Version other) {
        if (other == this) {
            return 0;
        } else {
            int diff = this._groupId.compareTo(other._groupId);
            if (diff == 0) {
                diff = this._artifactId.compareTo(other._artifactId);
                if (diff == 0) {
                    diff = this._majorVersion - other._majorVersion;
                    if (diff == 0) {
                        diff = this._minorVersion - other._minorVersion;
                        if (diff == 0) {
                            diff = this._patchLevel - other._patchLevel;
                        }
                    }
                }
            }

            return diff;
//... 생략

Comparable 인터페이스

package java.lang;
import java.util.*;

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

0개의 댓글