在 FreeMarker 模板引擎中,类型判断是非常关键的功能之一,尤其在处理动态数据、条件渲染、模板逻辑控制时。FreeMarker 提供了一系列内置的类型判断操作符(is_*),用于判断变量的类型,从而决定如何处理或渲染数据。

一、核心概念

操作符 说明 对应 Java 类型
is_string 判断是否为字符串类型 String
is_number 判断是否为数字类型 Number 子类(如 Integer、Double)
is_boolean 判断是否为布尔类型 Boolean
is_date 判断是否为日期类型 Date, Calendar 等时间类型
is_hash 判断是否为哈希结构(类似 Map) Map, JavaBean 对象
is_sequence 判断是否为有序集合(类似 List) List, Array

⚠️ 注意: FreeMarker 的变量类型在模板中是动态的,但底层仍依赖 Java 类型。类型判断主要用于控制模板逻辑,避免错误渲染。


二、操作步骤(非常详细)

步骤 1:准备数据模型

确保你传递给模板的数据中包含各种类型的数据,如字符串、数字、布尔值、日期、集合等。

Map<String, Object> model = new HashMap<>();
model.put("name", "Alice");               // String
model.put("age", 25);                       // Number
model.put("isStudent", true);              // Boolean
model.put("birth", new Date());            // Date
model.put("user", Map.of("id", 1, "name", "Bob"));  // Map / Hash
model.put("scores", Arrays.asList(90, 85, 95));      // List / Sequence

步骤 2:在模板中使用类型判断

2.1 判断是否为字符串 is_string

<#if name is "string">
  这是一个字符串:${name}
</#if>

输出:

这是一个字符串:Alice

2.2 判断是否为数字 is_number

<#if age is "number">
  年龄是数字:${age}
</#if>

输出:

年龄是数字:25

2.3 判断是否为布尔值 is_boolean

<#if isStudent is "boolean">
  是否是学生:${isStudent?string("是", "否")}
</#if>

输出:

是否是学生:是

2.4 判断是否为日期 is_date

<#if birth is "date">
  出生日期:${birth?date}
</#if>

输出:

出生日期:2025-07-28

2.5 判断是否为哈希结构 is_hash

<#if user is "hash">
  用户信息:
  ID:${user.id}, 名称:${user.name}
</#if>

输出:

用户信息:
ID:1, 名称:Bob

2.6 判断是否为有序集合 is_sequence

<#if scores is "sequence">
  成绩列表:
  <#list scores as score>
    ${score}
  </#list>
</#if>

输出:

成绩列表:
90
85
95

三、格式与语法说明

3.1 语法格式

<#if variable is "type">

其中 type 可以是:

  • string
  • number
  • boolean
  • date
  • hash
  • sequence

3.2 ?string("true", "false") 布尔值转换技巧

${isStudent?string("是", "否")}

用于将布尔值转为中文或自定义字符串。


四、常见错误与注意事项

4.1 错误使用类型判断

错误示例:

<#if 123 is "string">  <!-- 错误 -->

错误原因: 数字 123number 类型,不是 string

解决办法: 使用 ?string 转换后再判断:

<#assign str = 123?string>
<#if str is "string">

4.2 对 null 或未定义变量使用类型判断

错误示例:

<#if unknownVar is "string">  <!-- 变量未定义 -->

错误原因: 变量不存在,会抛出异常。

解决办法: 先判断是否存在:

<#if unknownVar??>
  <#if unknownVar is "string">
    ...
  </#if>
</#if>

4.3 is_hashis_sequence 判断 Map/Array

  • is_hash:适用于 Map、JavaBean
  • is_sequence:适用于 List、数组

注意:

  • Map 不能使用 ?size,应使用 size 属性
  • List 可以使用 ?size?first?last 等集合操作

五、使用技巧

5.1 使用 ?? 判断变量是否存在

<#if myVar??>
  变量存在
</#if>

5.2 多类型判断(模拟)

FreeMarker 不支持 is_string or is_number,可以使用多个 <#if> 嵌套:

<#if value is "string">
  字符串
<#elseif value is "number">
  数字
</#if>

5.3 使用 <#assign> 缓存类型判断结果

<#assign isString = (value is "string")>
<#if isString>
  是字符串
</#if>

避免重复判断,提高性能。


六、最佳实践

实践 说明
✅ 使用 ?? 判断变量是否存在 避免空指针异常
✅ 封装类型判断逻辑到宏中 提高复用性
✅ 在 Java 层处理复杂类型逻辑 提升模板可读性
✅ 使用 ?string("yes", "no") 显示布尔值 提高可读性
✅ 避免在 <#list> 中重复类型判断 优化性能

七、性能优化建议

优化点 说明
❌ 避免在 <#list> 中频繁类型判断 影响渲染效率
✅ 提前在 Java 层处理类型转换 Date -> String
✅ 使用 <#assign> 缓存判断结果 避免重复计算
✅ 对大集合进行预处理 避免在模板中做复杂逻辑

八、总结

操作符 用途 适用对象
is_string 判断是否为字符串 String
is_number 判断是否为数字 Number 子类
is_boolean 判断是否为布尔值 Boolean
is_date 判断是否为日期 Date, Calendar
is_hash 判断是否为 Map / JavaBean Map, Object
is_sequence 判断是否为 List / Array List, Array

九、附录:常用表达式示例汇总

表达式 示例说明
${value is "string"} 判断是否为字符串
${value is "number"} 判断是否为数字
${value is "boolean"} 判断是否为布尔值
${value is "date"} 判断是否为日期
${value is "hash"} 判断是否为 Map
${value is "sequence"} 判断是否为 List
${bool?string("是", "否")} 布尔值转中文
<#if var??> 判断变量是否存在

十、扩展建议

如需更复杂的类型处理(如类型转换、类型映射、多态逻辑),推荐:

  • 在 Java 层处理并封装逻辑,传入模板时使用统一格式
  • 使用宏(macro)封装类型判断逻辑,提高复用性和可维护性

例如:

<#macro displayValue value>
  <#if value is "string">
    字符串:${value}
  <#elseif value is "number">
    数字:${value}
  <#else>
    其他类型
  </#if>
</#macro>

结语: FreeMarker 的类型判断功能虽然基础,但掌握其用法和最佳实践,可以显著提升模板的健壮性和灵活性。结合 Java 层处理和模板逻辑控制,能构建出更强大、可维护的视图层逻辑。