你会重载Equals么?about how to override Equals

本文介绍了Equals方法的作用,默认的Object.Equals比较引用,ValueType.Equals比较数据结构。还阐述了重载Equals的要点,包括必须重载GetHashCode、处理null情形以及解决软件复用带来的问题,最后指出Equals对大项目很重要。

首先你必须了解Equals方法的作用。

默认的Object.Equals方法是比较两个应用是否指向同一对象:

 class A
 {
  public int a;
 }

A a1 = new A ();

a1.a = 10;

A a2 = new A ();

a2.a = 10;

这时,a1.Equals (a2)为False。

默认的ValueType.Equals方法是比较两个struct对象数据结构是否相同:

 struct A
 {
  public int a;
 }

A a1 = new A ();

a1.a = 10;

A a2 = new A ();

a2.a = 10;

这时,a1.Equals (a2)为True。

很多情况下,默认的Equals已经够用。

但各种情况下,你需要重载Equals,那么请看清一下条款在写Equals。

1。重载Equals必须重载GetHashCode

这是一条编译器的rule。如果你重载了Equals不重载GetHashCode,编译器会发出警报。

GetHashCode需要返回一个Int32值,这个值得规律很简单:

      如果两个对象相同,那么对象返回的HashCode必须相同。

      如果两个对象返回的HashCode不相同,那么这两个对象必定不相同。

这样就使得Equals的效率提高很多。因为GetHashCode通常成本比Equals小得多。

打个比方,如果要比较两个collection的对象是否一样(不分顺序),那么就要逐一比对他们内部包含的对象。这是一个费时的比对过程。GetHashCode在这个时候就显得尤为重要。

  public override int GetHashCode ()
  {
   int n = 0;
   foreach (object inner in this)
    n ^= inner.GetHashCode ();
   return n;
  }

  public override bool Equals (object obj)
  {
   if (obj.GetHashCode () != this.GetHashCode ())
    return false;
   // TODO:以下具体实现就不写了
  }

以上的Equals碰到不相同的情况很快就能排除掉,直接就return false了,碰到HashCode相同,那么两个对象可能相同,但不能就依此return true了,还要具体分析。

2。Equals碰到null时的情形

一般人在重写Equals的时候并未考虑碰到null时的情形,一旦被比对对象或者比对对象中一个为null,运行时错误就会产生。

其实解决方法很简单,把a.Equals (b)写成Object.Equals (a, b)。

Object的静态方法Equals会帮你处理null的对象。

所以重载运算符==,!=时也就要用到静态的Equals:

  public static bool operator == (T t1, T t2)
  {
   return Object.Equals (t1, t2);
  }
  public static bool operator != (T t1, T t2)
  {
   reutrn !Object.Equals (t1, t2);
  }


3。Equals对软件复用带来的影响

先来看一个例子:

 class A
 {
  public int a;
  public override int GetHashCode ()
  {
   return a.GetHashCode ();
  }
  public override bool Equals (object obj)
  {
   if (obj.GetHashCode () != this.GetHashCode ())
    return false;
   if (obj is A && (obj as A).a == a)
    return true;
   return false;
  }
 }
 class B
 {
  public long b;
  public override int GetHashCode ()
  {
   return b.GetHashCode();
  }
  public override bool Equals (object obj)
  {
   if (obj.GetHashCode () != this.GetHashCode ())
    return false;
   if (obj is B && (obj as B).b == b)
    return true;
   if (obj is A && (obj as A).a == b)
    return true;
   return false;
  }
 }

 class Program
 {
  static void Main (string[] args)
  {
   A a1 = new A ();
   a1.a = 20;
   B b1 = new B ();
   b1.b = 20;
   Console.WriteLine (a1.Equals (b1));
   Console.WriteLine (b1.Equals (a1));
  }
 }

在这个例子中A只认得自己人,新来的类B不仅认得自己人,还认得以前就有的A。

然后就出现了不该有的一幕:a1.Equals (b1)返回False,而b1.Equals (a1)返回了True。

这可如何是好。系统应该听那个呢?

这就是软件添加新类后遇到的麻烦。你必须改动已有的类来让他认识新来的。但如果已有的类无法修改呢?

办法也是有的。那就是建立一个EqualManager:

public class TwoTypes
{
  Type t1, t2;
  public TwoTypes (Type t1, Type t2)
  {
    this.t1 = t1; this.t2 = t2;
  }
  public override int GetHashCode ()
  {
    reutrn t1.GetHashCode() ^ t2.GetHashCode(); // 不分顺序
  }
  public override bool Equals (object obj)
  {
    if (obj.GetHashCode() != this.GetHashCode())
        return false;
    if (obj is TwoTypes)
    {
       Type tt1 = (obj as TwoTypes).t1, tt2 = (obj as TwoTypes).t2;
       if (tt1 == t1 && tt2 == t2)
         return true;
       if (tt1 == t2 && tt2 == t1)
         return true;
    }
    return false;
  }
}
public class EqualManager : IComparer  // singleton
{
  Hashtable ht = new Hashtable ();
  private EqualManager ()
  {
  }
  public readonly EqualManager Instance = new EqualManager ();
  public int Compare (object o1, object o2)
  {
    TwoTypes tt = new TwoTypes (o1.GetType(), o2.GetType());
    if (ht.ComtainsKey (tt))
    {
      return (ht[tt] as IComparer).Compare (o1, o2);
    }
    else
      return false;
  }
  public void RegisterComparer (Type t1, Type t2, IComparer comp)
  {
    this.ht.Add (new TwoTypes (t1, t2), comp);
  }
}

看看代码就知道具体实现了,需要比对服务的类只要这样重写Equals:
public override bool Equals (object obj)
{
  return EqualManager.Instance.Compare (this, obj) == 0;
}
然后具体的实现就要依赖一个IComparer了。这样既实现了程序的可扩展性。

总结语:
Equals对于较小的项目确实无所谓,但对于较大的项目就显得重要了,毕竟这是底层的东西。幸好它比较简单,各位只要掌握以上三点,我想Equals应该不在话下了把。

只是无聊时写的无聊文章,如有不对的地方敬请更正。

逐行解析代码,不要修改和遗漏源代码,去除行号: /** 2434 * Returns the model year of the GNSS hardware and software build, or 0 if the model year 2435 * is before 2016. 2436 */ 2437 public int getGnssYearOfHardware() { 2438 try { 2439 return mService.getGnssYearOfHardware(); 2440 } catch (RemoteException e) { 2441 throw e.rethrowFromSystemServer(); 2442 } 2443 } 2444 2445 /** 2446 * Returns the model name (including vendor and hardware/software version) of the GNSS hardware 2447 * driver, or null if this information is not available. 2448 * 2449 * <p>No device-specific serial number or ID is returned from this API. 2450 */ 2451 @Nullable 2452 public String getGnssHardwareModelName() { 2453 try { 2454 return mService.getGnssHardwareModelName(); 2455 } catch (RemoteException e) { 2456 throw e.rethrowFromSystemServer(); 2457 } 2458 } 2459 2460 /** 2461 * Returns the current list of GNSS antenna infos, or null if unknown or unsupported. 2462 * 2463 * @see #getGnssCapabilities() 2464 */ 2465 @Nullable 2466 @SuppressLint("NullableCollection") 2467 public List<GnssAntennaInfo> getGnssAntennaInfos() { 2468 try { 2469 return mService.getGnssAntennaInfos(); 2470 } catch (RemoteException e) { 2471 throw e.rethrowFromSystemServer(); 2472 } 2473 } 2474 2475 /** 2476 * Retrieves information about the current status of the GPS engine. This should only be called 2477 * from within the {@link GpsStatus.Listener#onGpsStatusChanged} callback to ensure that the 2478 * data is copied atomically. 2479 * 2480 * The caller may either pass in an existing {@link GpsStatus} object to be overwritten, or pass 2481 * null to create a new {@link GpsStatus} object. 2482 * 2483 * @param status object containing GPS status details, or null. 2484 * @return status object containing updated GPS status. 2485 * 2486 * @deprecated GpsStatus APIs are deprecated, use {@link GnssStatus} APIs instead. No longer 2487 * supported in apps targeting S and above. 2488 */ 2489 @Deprecated 2490 @RequiresPermission(ACCESS_FINE_LOCATION) 2491 public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) { 2492 if (Compatibility.isChangeEnabled(BLOCK_GPS_STATUS_USAGE)) { 2493 //#ifdef OPLUS_ARCH_INJECT 2494 //Huaqiu.Lin@CONNECTIVITY.GPS.LOCATION.STABILITY, 2021/08/18, Add for code arch. 2495 mLocationManagerExt.handleCompatibilityException(mContext); 2496 // #endif /* OPLUS_ARCH_INJECT */ 2497 throw new UnsupportedOperationException( 2498 "GpsStatus APIs not supported, please use GnssStatus APIs instead"); 2499 } 2500 2501 GnssStatus gnssStatus = GpsStatusTransport.sGnssStatus; 2502 int ttff = GpsStatusTransport.sTtff; 2503 if (gnssStatus != null) { 2504 if (status == null) { 2505 status = GpsStatus.create(gnssStatus, ttff); 2506 } else { 2507 status.setStatus(gnssStatus, ttff); 2508 } 2509 } else if (status == null) { 2510 // even though this method is marked as nullable, legacy behavior was to never return 2511 // a null result, and there are applications that rely on this behavior. 2512 status = GpsStatus.createEmpty(); 2513 } 2514 return status; 2515 } 2516 2517 /** 2518 * Adds a GPS status listener. 2519 * 2520 * @param listener GPS status listener object to register 2521 * @return true if the listener was successfully added 2522 * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present 2523 * 2524 * @deprecated Use {@link #registerGnssStatusCallback(GnssStatus.Callback, Handler)} or {@link 2525 * #registerGnssStatusCallback(Executor, GnssStatus.Callback)} instead. 2526 */ 2527 @Deprecated 2528 @RequiresPermission(ACCESS_FINE_LOCATION) 2529 public boolean addGpsStatusListener(GpsStatus.Listener listener) { 2530 if (Compatibility.isChangeEnabled(BLOCK_GPS_STATUS_USAGE)) { 2531 //#ifdef OPLUS_ARCH_INJECT 2532 //Huaqiu.Lin@CONNECTIVITY.GPS.LOCATION.STABILITY, 2021/08/18, Add for code arch. 2533 mLocationManagerExt.handleCompatibilityException(mContext); 2534 // #endif /* OPLUS_ARCH_INJECT */ 2535 throw new UnsupportedOperationException( 2536 "GpsStatus APIs not supported, please use GnssStatus APIs instead"); 2537 } 2538 2539 GnssLazyLoader.sGnssStatusListeners.addListener(listener, 2540 new GpsStatusTransport(new HandlerExecutor(new Handler()), mContext, listener)); 2541 return true; 2542 } 2543 2544 /** 2545 * Removes a GPS status listener. 2546 * 2547 * @param listener GPS status listener object to remove 2548 * 2549 * @deprecated use {@link #unregisterGnssStatusCallback(GnssStatus.Callback)} instead. No longer 2550 * supported in apps targeting S and above. 2551 */ 2552 @Deprecated 2553 public void removeGpsStatusListener(GpsStatus.Listener listener) { 2554 if (Compatibility.isChangeEnabled(BLOCK_GPS_STATUS_USAGE)) { 2555 //#ifdef OPLUS_ARCH_INJECT 2556 //Huaqiu.Lin@CONNECTIVITY.GPS.LOCATION.STABILITY, 2021/08/18, Add for code arch. 2557 mLocationManagerExt.handleCompatibilityException(mContext); 2558 // #endif /* OPLUS_ARCH_INJECT */ 2559 throw new UnsupportedOperationException( 2560 "GpsStatus APIs not supported, please use GnssStatus APIs instead"); 2561 } 2562 2563 GnssLazyLoader.sGnssStatusListeners.removeListener(listener); 2564 } 2565 2566 /** 2567 * Registers a GNSS status callback. This method must be called from a {@link Looper} thread, 2568 * and callbacks will occur on that looper. 2569 * 2570 * <p>See {@link #registerGnssStatusCallback(Executor, GnssStatus.Callback)} for more detail on 2571 * how this method works. 2572 * 2573 * @param callback the callback to register 2574 * @return {@code true} always 2575 * 2576 * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present 2577 * 2578 * @deprecated Use {@link #registerGnssStatusCallback(GnssStatus.Callback, Handler)} or {@link 2579 * #registerGnssStatusCallback(Executor, GnssStatus.Callback)} instead. 2580 */ 2581 @Deprecated 2582 @RequiresPermission(ACCESS_FINE_LOCATION) 2583 public boolean registerGnssStatusCallback(@NonNull GnssStatus.Callback callback) { 2584 return registerGnssStatusCallback(callback, null); 2585 } 2586 2587 /** 2588 * Registers a GNSS status callback. 2589 * 2590 * <p>See {@link #registerGnssStatusCallback(Executor, GnssStatus.Callback)} for more detail on 2591 * how this method works. 2592 * 2593 * @param callback the callback to register 2594 * @param handler the handler the callback runs on 2595 * @return {@code true} always 2596 * 2597 * @throws IllegalArgumentException if callback is null 2598 * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present 2599 */ 2600 @RequiresPermission(ACCESS_FINE_LOCATION) 2601 public boolean registerGnssStatusCallback( 2602 @NonNull GnssStatus.Callback callback, @Nullable Handler handler) { 2603 if (handler == null) { 2604 handler = new Handler(); 2605 } 2606 2607 //#ifdef OPLUS_EXTENSION_HOOK 2608 //zengziping@ANDROID.ARCHDESIGN, 2021/07/08, Add for AOSP hook 2609 if (!mLocationManagerExt 2610 .checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), "registerGnssStatusCallback")) { 2611 return false; 2612 } 2613 //#endif /* OPLUS_EXTENSION_HOOK */ 2614 return registerGnssStatusCallback(new HandlerExecutor(handler), callback); 2615 }
最新发布
12-30
<think> 在 MyBatis 中判断传入参数是否等于特定值时,可以使用 `equals` 方法,但需要注意 **空指针风险** 和 **表达式语法**。以下是具体分析: --- ### 一、使用 `.equals()` 的可行性 在 MyBatis 的动态 SQL 中,可以通过 OGNL 调用 Java 对象的 `.equals()` 方法进行值比较: ```xml <if test="'yes'.equals(status)"> AND flag = 'ACTIVE' </if> ``` **优势**: 1. **避免空指针**:`'yes'.equals(status)` 即使 `status` 为 `null` 也不会抛异常,而是返回 `false`; 2. **类型安全**:显式调用方法确保比较逻辑清晰。 --- ### 二、正确使用 `.equals()` 的示例 #### 1. 字符串比较 ```xml <!-- 安全写法:常量在前,参数在后 --> <if test="'yes'.equals(status)"> ... </if> ``` #### 2. 数字比较(需先转字符串) 若参数为数字类型(如 `Integer`),需先调用 `toString()` 转换为字符串: ```xml <if test="'1'.equals(type.toString())"> ... </if> ``` **注意**:若 `type` 可能为 `null`,需先判空: ```xml <if test="type != null and '1'.equals(type.toString())"> ... </if> ``` --- ### 三、为什么不直接 `status.equals('yes')`? **空指针风险**:若 `status` 为 `null`,则 `status.equals('yes')` 会抛出 `NullPointerException`。 ```xml <!-- 危险写法:可能因 status=null 崩溃 --> <if test="status.equals('yes')"> ... </if> <!-- 不推荐! --> ``` --- ### 四、对比方案:`==` 与 `.equals()` | **方式** | 示例 | 安全性 | 类型处理 | |------------------|--------------------------|--------|------------------------| | `==` + `.toString()` | `status == 'yes'.toString()` | 中 | 需统一类型 | | `.equals()` | `'yes'.equals(status)` | 高 | 自动处理字符串比较 | **结论**: - **优先用 `.equals()`**:更安全且代码更直观; - 数字比较需额外转换:因 `.equals()` 是字符串方法,数字需显式转字符串。 --- ### 五、完整案例 ```java // Mapper 方法 List<User> getUsers( @Param("status") String status, @Param("role") Integer role ); ``` ```xml <select id="getUsers" resultType="User"> SELECT * FROM user WHERE 1=1 <!-- 字符串比较:安全写法 --> <if test="'active'.equals(status)"> AND account_status = 'ACTIVE' </if> <!-- 数字比较:先判空再转字符串 --> <if test="role != null and '1'.equals(role.toString())"> AND role_type = 1 </if> </select> ``` --- ### 六、特别提醒 1. **OGNL 限制**: MyBatis 使用的 OGNL 不支持部分 Java 语法(如 `==` 对字符串的比较行为不一致[^1]),因此 `.equals()` 是更可靠的选择。 2. **枚举比较**: 若参数为枚举类型,可比较其 `name()` 或 `ordinal()`: ```xml <if test="'ACTIVE'.equals(userStatus.name())"> ... </if> ``` --- §§相关问题§§ 1. MyBatis 中如何安全地比较枚举类型参数? 2. 除了 `.equals()`,还有哪些方法解决 OGNL 类型匹配问题? 3. 如何优化 MyBatis 动态 SQL 中的多重条件判断? 4. MyBatis 的 OGNL 表达式支持哪些运算符和内置方法? </think>在 MyBatis 中判断传入参数是否等于特定值时,**可以使用 `equals()` 方法**,且这是更安全可靠的方式。以下是具体实现方法和注意事项: --- ### 一、使用 `equals()` 的正确方式 #### 1. 基本语法 ```xml <if test="'固定值'.equals(参数)"> SQL条件 </if> ``` #### 2. 实际示例 ```xml <!-- 判断字符串参数等于 'yes' --> <if test="'yes'.equals(status)"> AND flag = 'ACTIVE' </if> <!-- 判断数字参数等于 1(需转为字符串) --> <if test="'1'.equals(type.toString())"> AND category = 1 </if> ``` --- ### 二、为什么推荐 `equals()` 而不是 `==` 1. **避免空指针异常** `'yes'.equals(status)` 在 `status=null` 时返回 `false`,而 `status.equals('yes')` 在 `status=null` 时会抛出异常。 2. **解决类型不一致问题** OGNL 对 `==` 的比较要求严格类型匹配(如 `String` 和 `Integer` 不相等)[^1],而 `equals()` 更灵活。 3. **字符串比较更可靠** 特别是当参数可能是 `String`、`CharSequence` 等不同类型时。 --- ### 三、完整案例(带空值检查) ```xml <select id="getData" resultType="Data"> SELECT * FROM table <where> <!-- 安全写法:常量在前 --> <if test="status != null and 'yes'.equals(status)"> AND flag = 'ACTIVE' </if> <!-- 数字比较需转换 --> <if test="type != null and '1'.equals(type.toString())"> AND category = 1 </if> </where> </select> ``` --- ### 四、常见错误示例 ```xml <!-- 错误1:可能空指针 --> <if test="status.equals('yes')"> ... </if> <!-- 错误2:类型不匹配 --> <if test="type == 1"> ... </if> <!-- 若type是String类型会失败 --> <!-- 错误3:未判空 --> <if test="'yes'.equals(status)"> ... </if> <!-- status为null时漏处理 --> ``` --- ### 五、对比总结 | **方法** | 示例 | 安全性 | 推荐度 | |----------------------|--------------------------|--------|--------| | **`equals()`** | `'yes'.equals(status)` | ⭐⭐⭐⭐ | ✅ 最佳 | | **`==` + `.toString()`** | `status == 'yes'.toString()` | ⭐⭐⭐ | ✅ 可行 | | **直接 `==`** | `status == 'yes'` | ⭐ | ❌ 避免 | --- ### 六、特殊场景:枚举比较 ```java public enum UserStatus { ACTIVE, INACTIVE } ``` ```xml <!-- 比较枚举值 --> <if test="'ACTIVE'.equals(status.name())"> AND state = 'ACTIVE' </if> ``` > **实测结论**:在 MyBatis 3.5+ 版本中,`'常量'.equals(参数)` 是判断值相等的**最稳定方案**,尤其当参数来源复杂(如 Map、DTO、基本类型)时[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值