Search
๐Ÿ—„๏ธ

ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์‹ค์ œ ๊ฐ’์„ ๊ฐ€์ง€๋Š” ์‹œ์ 

์ƒ์„ฑ์ผ
2022/08/18
ํƒœ๊ทธ
Spring
JPA

๋ฐฐ๊ฒฝ

์ €ํฌ ๋ชจ๋ฝ ํ”„๋กœ์ ํŠธ์˜ ์„œ๋น„์Šค ๋กœ์ง ๊ฐœ๋ฐœ ์ค‘, equals ๋กœ ๋น„๊ตํ•ด์•ผ ํ•  ๋กœ์ง์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. ์กฐ๊ธˆ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ๋Š” ํˆฌํ‘œ๋ฅผ ์ƒ์„ฑํ•œ ํ˜ธ์ŠคํŠธ์™€, ์ˆ˜์ •/์‚ญ์ œ๋ฅผ ์š”์ฒญํ•œ ๋ฉค๋ฒ„๊ฐ€ ๊ฐ™์€์ง€ ํ™•์ธํ•˜๋Š” ๋กœ์ง์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ findById ๋กœ ๊ฐ์ฒด๋ฅผ ๋ถˆ๋Ÿฌ์„œ ๋น„๊ต๋ฅผ ํ•˜๋ ค๊ณ  ํ•˜๋Š”๋ฐ ์›ํ•˜๋Š”๋Œ€๋กœ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š๊ณ  ๊ณ„์† ์ผ์น˜ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์‘๋‹ต์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ equals ๋‚ด๋ถ€์— ๋””๋ฒ„๊ทธ๋ฅผ ์ฐ์–ด๋ณด๋‹ˆ ์ด์ƒํ•˜๊ฒŒ ๊ณ„์† ์ธ์ž๋กœ ๋ฐ›์€ ๊ฐ์ฒด๊ฐ€ ํ”„๋ก์‹œ ๊ฐ์ฒด๋กœ ์ €์žฅ๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
JPA ๋Š” ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅํ•˜๊ณ  ์ด๋ฅผ ๋‹ค์–‘ํ•œ ๋ถ€๋ถ„์—์„œ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•๋ถ„์— DB ์ปค๋„ฅ์…˜์„ ํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ค„์ด๊ณ , ์ด ๊ธฐ๋Šฅ์ด JPA ์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์กฐํšŒ ์‹œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์กฐํšŒ๋ฅผ ์›ํ•˜๋Š” ๊ฐ์ฒด๊ฐ€ ์—†์œผ๋ฉด ๋ฐ˜๋“œ์‹œ DB ๋ฅผ ์ฐ”๋Ÿฌ ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ๊ฐ€์ง€๊ณ  ์™€์•ผ ํ•˜๊ณ , ์ €ํฌ๊ฐ€ ๋งŒ๋“  ๋กœ์ง์—์„œ๋Š” ํ•ด๋‹น ๊ฐ์ฒด๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์—†๋‹ค๊ณ  ํŒ๋‹จํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ด์™€ ๋”๋ถˆ์–ด ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ•˜๋Š” SQL ๊ตฌ๋ฌธ์ด ๋กœ๊ทธ์— ์ฐํ˜”์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๊ณ„์† ํ”„๋ก์‹œ ๊ฐ์ฒด๋กœ ๋‚จ์•„์žˆ์—ˆ๊ณ , ์ด ๊ณผ์ •์—์„œ equals ๋‚ด๋ถ€ ๊ฐ์ฒด ๋น„๊ต ๋กœ์ง์—์„œ false ๋กœ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.
๋„๋Œ€์ฒด ๋ฌธ์ œ๊ฐ€ ๋ญ”์ง€, ๋””๋ฒ„๊ทธ๋ฅผ ์ฐ์–ด๋„ ํ•ด๊ฒฐ์ด ์•ˆ๋˜์—ˆ๊ณ  ์ฝ”์น˜๋ถ„๋“ค๊ป˜ ๋„์›€์„ ์š”์ฒญํ•˜์—ฌ ๋‹คํ–‰ํžˆ ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ์•Œ๊ฒŒ๋œ ๋‚ด์šฉ๊ณผ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ณต์œ ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์„ธํŒ…

์ €ํฌ๋Š” ํˆฌํ‘œ์™€ ๋ฉค๋ฒ„ ๋„๋ฉ”์ธ ์‚ฌ์ด์—์„œ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ์˜€๋Š”๋ฐ, ํ•ด๋‹น ๋ฌธ์ œ๋Š” 1:N ์—ฐ๊ด€๊ด€๊ณ„์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ด๊ธฐ ๋•Œ๋ฌธ์— ์กฐ๊ธˆ ๋” ๋ณดํŽธ์ ์ธ ์˜ˆ์‹œ๋ฅผ ๋“ค๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. ํˆฌํ‘œ๋ฅผ ๋ฉค๋ฒ„๋กœ ๋ฐ”๊พธ๊ณ , ๋ฉค๋ฒ„๋ฅผ ํŒ€์œผ๋กœ ๋ฐ”๊พธ์–ด ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰ ๋ฉค๋ฒ„:ํˆฌํ‘œ=1:N ์˜ ๊ด€๊ณ„๋ฅผ ํŒ€:๋ฉค๋ฒ„=1:N ์œผ๋กœ ๋ฐ”๊พผ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.
@Entity @NoArgsConstructor @Getter public class Team { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Team team = (Team)o; return Objects.equals(getId(), team.getId()) && Objects.equals(getName(), team.getName()); } @Override public int hashCode() { return Objects.hash(id, name); } }
Java
๋ณต์‚ฌ
๋น„๊ต๋ฅผ ์œ„ํ•ด equals ๋ฅผ ์žฌ์ •์˜ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
@Entity @NoArgsConstructor @Getter public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ManyToOne(fetch = FetchType.LAZY) private Team team; public boolean isTeam(Team team) { return this.team.equals(team); } }
Java
๋ณต์‚ฌ
์ €ํฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋˜ ํ™˜๊ฒฝ๊ณผ ๋™์ผํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด 1:N @ManyToOne ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
@Service @RequiredArgsConstructor public class MorakService { private final MemberRepository memberRepository; private final TeamRepository teamRepository; public boolean test(Long memberId, Long teamId) { Member member = memberRepository.findById(teamId).orElseThrow(); Team team = teamRepository.findById(memberId).orElseThrow(); return member.isTeam(team); } }
Java
๋ณต์‚ฌ
member ์™€ team ์„ ์กฐํšŒํ•˜๊ณ  ๋น„๊ต ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ ์ง€์  ํ™•์ธ

๋ฌธ์ œ ์ง€์  ํ™•์ธ์„ ์œ„ํ•ด ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ ธ์Šต๋‹ˆ๋‹ค. ๋”๋ฏธ ๋ฐ์ดํ„ฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค.
Team
id
name
1
morak
Member
id
name
team_id
1
eden
1
@Test @DisplayName("๊ฐ™์€ ํŒ€์ธ์ง€ ํ™•์ธํ•œ๋‹ค.") void test() { //given Long memberId = 1L; Long teamId = 1L; //when boolean isSameTeam = morakService.test(memberId, teamId); //then assertThat(isSameTeam).isTrue(); }
Java
๋ณต์‚ฌ
ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ฆฌ๋ฉด ์กฐํšŒํ•œ ํŒ€(id=1)๊ณผ ๋ฉค๋ฒ„์— ์ €์žฅ๋œ ํŒ€(id=1)์ด ๊ฐ™์€๋ฐ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

๊ณผ์ •

์ฐจ๊ทผ์ฐจ๊ทผ ์„œ๋น„์Šค ๋กœ์ง๋ถ€ํ„ฐ ๋””๋ฒ„๊ทธ๋ฅผ ๋Œ๋ ค ๋ณด์•˜์Šต๋‹ˆ๋‹ค.
์กฐํšŒ ์ดํ›„ ์ฐ์€ ๋””๋ฒ„๊ทธ์—์„œ member ์˜ team ๊ณผ team ๋‘˜ ๋‹ค ํ”„๋ก์‹œ ๊ฐ์ฒด๋กœ ์„ค์ •๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. member ์˜ team ์€ member ์กฐํšŒ ์‹œ fetch type ์„ lazy ๋กœ ํ•ด์„œ ์ดํ•ด๊ฐ€ ๋˜์—ˆ์ง€๋งŒ, team ์กฐํšŒ ์‹œ์—๋„ team ์ด ํ”„๋ก์‹œ ๊ฐ์ฒด๋กœ ์žˆ๋Š” ๊ฒƒ์ด ์ด์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ํ˜„์ƒ์€ ์ผ๋‹จ ๋‚˜์ค‘์— ํ™•์ธํ•œ๋‹ค ํ•ด๋„, ๋ฉค๋ฒ„์˜ ํŒ€๊ณผ ์กฐํšŒํ•œ ํŒ€์€ ๋‘˜ ๋‹ค ํ”„๋ก์‹œ ๊ฐ์ฒด์ด๊ณ  ๊ฐ™์€ ์ฃผ์†Œ๊ฐ’์„ ๊ฐ€์ง€๋Š”๋ฐ ์™œ false ๊ฐ€ ๋‚˜ํƒ€๋‚ฌ๋Š”์ง€ ์ดํ•ด๊ฐ€ ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
๋‚ด๋ถ€ ๋กœ์ง์œผ๋กœ ๋””๋ฒ„๊ทธ๋ฅผ ๋” ๋“ค์–ด๊ฐ€ ๋ณด์•˜์Šต๋‹ˆ๋‹ค.
โ€ข
member.isTeam(team) ๋‚ด๋ถ€
ํ˜„์žฌ๊นŒ์ง€๋„ member ์˜ team ๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋“ค์–ด์˜จ team ๋ชจ๋‘ ๊ฐ™์€ ์ฃผ์†Œ๊ฐ’์„ ๊ฐ€์ง€๋Š” ๋™์ผํ•œ ํ”„๋ก์‹œ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
โ€ข
Override ํ•œ team.equals(team) ๋‚ด๋ถ€
this ์ธ โ€˜member ์˜ teamโ€™ ์ด Team ๊ฐ์ฒด๋กœ ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋“ค์–ด์˜จ team(๋ณ€์ˆ˜๋ช… o)์€ ์—ฌ์ „ํžˆ ํ”„๋ก์‹œ ๊ฐ์ฒด์˜€๊ณ ,ย getClass() != o.getClass()ย ์—์„œ false ๋กœ ๊ฑธ๋Ÿฌ์ง„ ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.
์–ด๋–ป๊ฒŒ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์ด ํ”„๋ก์‹œ๋ฅผ ์‹ค์ œ ๋ฐ์ดํ„ฐ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค๊ฒŒ ๋˜๋Š” ๊ฒƒ์ผ๊นŒ?
ํ”„๋ก์‹œย ๊ฐ์ฒด์˜ย ๋ฉ”์„œ๋“œ๊ฐ€ย ํ˜ธ์ถœ๋˜๋ฉดย ProxyConfigurationย ๋‚ด๋ถ€์˜ย InterceptorDispatcherย ํด๋ž˜์Šค์˜ย interceptย ๋ฉ”์„œ๋“œ๊ฐ€ย ๋จผ์ € ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
public interface ProxyConfiguration { // ... interface Interceptor { @RuntimeType Object intercept(@This Object instance, @Origin Method method, @AllArguments Object[] arguments) throws Throwable; } class InterceptorDispatcher { @RuntimeType public static Object intercept( @This final Object instance, @Origin final Method method, @AllArguments final Object[] arguments, @StubValue final Object stubValue, @FieldValue(INTERCEPTOR_FIELD_NAME) Interceptor interceptor ) throws Throwable { if ( interceptor == null ) { // 1 if ( method.getName().equals( "getHibernateLazyInitializer" ) ) { return instance; } else { return stubValue; } } else { return interceptor.intercept( instance, method, arguments );// 2 } } } }
Java
๋ณต์‚ฌ
1 ์—์„œ interceptor ๊ฐ€ null ์ด ์•„๋‹ˆ๊ธฐ๋•Œ๋ฌธ์— 2๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น interceptor๋Š” ProxyConfiguration.Interceptorย ์ธํ„ฐํŽ˜์ด์Šค์—์„œย ์„ ์–ธ๋œ intercept(instance,ย method,ย arguments)ย ๋ฉ”์„œ๋“œ๋ฅผย ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น interceptor ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋Š” ์•„๋ž˜์˜ ByteBuddyInterceptor ์ž…๋‹ˆ๋‹ค.
public class ByteBuddyInterceptor extends BasicLazyInitializer implements ProxyConfiguration.Interceptor { // ... public Object intercept(Object proxy, Method thisMethod, Object[] args) throws Throwable { Object result = this.invoke( thisMethod, args, proxy ); if ( result == INVOKE_IMPLEMENTATION ) { Object target = getImplementation(); final Object returnValue; try { if ( ReflectHelper.isPublic( persistentClass, thisMethod ) ) { if ( !thisMethod.getDeclaringClass().isInstance(target) ) { // ... } returnValue = thisMethod.invoke( target, args );// - 2 } // ... return returnValue; } catch (InvocationTargetException ite) { throw ite.getTargetException(); } } else { return result; } } protected final Object invoke(Method method, Object[] args, Object proxy) throws Throwable { String methodName = method.getName(); int params = args.length; if (params == 0) { // ... } else if (params == 1) { if (!overridesEquals && "equals".equals(methodName)) {// - 1 return args[0] == proxy; } else if (method.equals(setIdentifierMethod)) { initialize(); setIdentifier((Serializable) args[0]); return INVOKE_IMPLEMENTATION; } } } }
Java
๋ณต์‚ฌ
interceptor() ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ๋จผ์ € invoke() ๋ฉ”์„œ๋“œ๋กœ equals ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ–ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์ €ํฌ๋Š” equals ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ–ˆ๊ธฐ๋•Œ๋ฌธ์— 1๋ฒˆ์˜ !overridesEquals() ์—์„œ false ๊ฐ€ ๋˜๊ณ  INVOKE_IMPLEMENTATION ์„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.ย ์ดํ›„ getImplementation() ์œผ๋กœ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ์•ž์„œ ํ˜ธ์ถœํ•œย ๋ฉ”์„œ๋“œ(equals)๊ฐ€ 2๋ฒˆ์˜ thisMethod.invoke( target, args ); ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

1. fetch type EAGER
์ง€์—ฐ ๋กœ๋”ฉ์„ ํ•˜์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ๋•Œ๋ฌธ์— ์ดํ›„์— ๋ถˆ๋Ÿฌ์˜ค๋Š” team ๋„ ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ ์‹ค์ œ ๊ฐ์ฒด๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ํŒ€ ๋‚ด๋ถ€ ํšŒ์˜ ๊ฒฐ๊ณผ, ๊ตณ์ด ๋งค๋ฒˆ EAGER ๋กœ ๋ถˆ๋Ÿฌ์˜ฌ ํ•„์š”์„ฑ์„ ๋А๋ผ์ง€ ๋ชปํ•˜์—ฌ ๋‹ค๋ฅธ ๋ฐฉ์•ˆ์„ ์ƒ๊ฐํ•˜์˜€์Šต๋‹ˆ๋‹ค.
2. find ํ•˜๋Š” ์ˆœ์„œ๋ฅผ ๋ฐ”๊พผ๋‹ค.
Team team = teamRepository.findById(memberId).orElseThrow(); Member member = memberRepository.findById(teamId).orElseThrow();
Plain Text
๋ณต์‚ฌ
team ์„ ๋จผ์ € ๋ถˆ๋Ÿฌ ํ•ด๋‹น team ์ด ๋ฐ”๋กœ ์‹ค์ œ ๊ฐ์ฒด๊ฐ€ ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ๊ทผ๋ณธ์ ์ธ ํ•ด๊ฒฐ์ฑ…์ด ์•„๋‹ˆ๊ธฐ๋•Œ๋ฌธ์— ๋งค๋ฒˆ ์ˆœ์„œ๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ๋˜ ์ฐพ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.
3. equals ๋ฅผ override ํ•˜์ง€ ์•Š๊ธฐ
ByteBuddyInterceptor ์˜ invoke() ๋ฉ”์„œ๋“œ์—์„œ !overrideEquals ๊ฐ€ true ๊ฐ€ ๋˜์–ด ๋‘ ๊ฐ์ฒด ๋ชจ๋‘ ํ”„๋ก์‹œ ๊ฐ์ฒด๋กœ ๋‚จ์•„ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ผ๋ฆฌ์˜ ๋น„๊ต๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
์•„๋ฌด๋ฆฌ JPA ๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๋™์ผ์„ฑ์„ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๋‹ค์ง€๋งŒ ์ œ์–ด๊ถŒ์ด JPA ์— ๋„˜์–ด๊ฐ€๋Š” ๊ฒƒ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค.
4. equals override ์ปค์Šคํ…€ํ•˜๊ธฐ
override ํ•œ equals ์—์„œ getClass() != o.getClass() ๋ถ€๋ถ„์„ ์ง€์›Œ์ฃผ๊ณ  !(o instanceof Team) ์„ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค. ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์ƒ์†์„ ์ด์šฉํ•˜์—ฌ ์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— instanceof ํ‚ค์›Œ๋“œ๋กœ ์ถฉ๋ถ„ํžˆ ๋งŒ์กฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
public boolean equals(Object o) { if (this == o) { return true; } if (o == null !(o instanceof Team)) { return false; } Team team = (Team) o; return Objects.equals(id, team.getId()) && Objects.equals(name, team.getName()); }
Kotlin
๋ณต์‚ฌ