2011-03-07 1 views
3

foo 메서드를 내 컨트롤 외부에있는 기존 유형 A에 제공한다고 가정합니다. 필자가 아는 한, 스칼라에서 이것을 수행하는 표준 방법은 A에서 foo을 구현하는 유형으로 암시 적 변환을 구현하는 것입니다. 이제 기본적으로 두 가지 옵션이 표시됩니다. 암시 적 메서드의 중간 형식을 구현하는 방법은 무엇입니까?

  1. 는 목적을 위해 별도의, 어쩌면 숨겨진 클래스를 정의 :

    protected class Fooable(a : A) { 
        def foo(...) = { ... } 
    } 
    implicit def a2fooable(a : A) = new Fooable(a) 
    
  2. 익명 클래스 인라인 정의 :

    implicit def a2fooable(a : A) = new { def foo(...) = { ... } } 
    

변형 2) 확실히 덜 보일러는 특히 많은 유형의 매개 변수가 발생하는 경우 반면에, (개념적으로) 변환 당 하나의 클래스가 만들어지기 때문에 더 많은 오버 헤드가 발생해야한다고 생각합니다. 1에서 전 세계적으로 하나의 클래스와 반대되는 것입니다.

일반적인 가이드 라인이 있습니까? 컴파일러/VM은 2)의 오버 헤드를 제거하기 때문에 아무런 차이가 있습니까?

답변

6

필자는 1과 2가 동일한 바이트 코드로 컴파일 될 것이라고 생각합니다 (케이스 2에서 생성 된 클래스 이름 제외). Fooable이 암시 적으로 A를 Fooable로 변환 할 수있는 경우에만 (그리고 Fooable을 직접 만들고 사용하지 않으려는 경우) 옵션 2를 사용합니다.

그러나 A 내가 A.

UPDATE에 동작을 추가하는 대신 암시 적 변환의 특성을 이용하여 고려할 것 (A를 의미하는 것은 아닌 서브 클래스 수있는 자바 라이브러리 클래스는 아니다) : 나는 내 대답을 재고해야 . 변형 2가 리플렉션 (리눅스에서 2.8.1 스칼라)을 사용하는 것으로 판명 되었기 때문에, 당신의 코드의 변형 1을 사용할 것입니다. 라는 이름의 클래스

소스 코드를 익명 클래스

class NamedClass { def Foo : String = "foo" } 

object test { 
    implicit def StrToFooable(a: String) = new NamedClass 
    def main(args: Array[String]) { println("bar".Foo) } 
} 

소스 코드 :

내가 동일한 코드의 두 버전을 컴파일

는, 결과 여기에 JD-GUI와 자바를 디 컴파일하고

object test { 
    implicit def StrToFooable(a: String) = new { def Foo : String = "foo" } 

    def main(args: Array[String]) { println("bar".Foo) } 
}  

java-gui를 사용하여 java로 컴파일 및 디 컴파일. 은 "이름"버전이 자바에 디 컴파일 된 가져옵니다 NamedClass.class 생성

public class NamedClass 
    implements ScalaObject 
{ 
    public String Foo() 
    { 
    return "foo"; 
    } 
} 

익명 테스트를 생성을 $$ 곧 다음과 같은 자바 디 컴파일됩니다 $ (1) 클래스

public final class test$$anon$1 
{ 
    public String Foo() 
    { 
    return "foo"; 
    } 
} 

그래서 거의 익명의 "final"을 제외하고는 동일합니다. (익명의 클래스를 시도하고 하위 클래스로 만들지 않으려 고합니다.)

그러나 호출 사이트에서 나는 "이라는"버전

public void main(String[] args) 
{ 
    Predef..MODULE$.println(StrToFooable("bar").Foo()); 
} 

이 자바를 얻고 익명

public void main(String[] args) { 
    Object qual1 = StrToFooable("bar"); Object exceptionResult1 = null; 
    try { 
     exceptionResult1 = reflMethod$Method1(qual1.getClass()).invoke(qual1, new Object[0]); 
     Predef..MODULE$.println((String)exceptionResult1); 
     return; 
    } catch (InvocationTargetException localInvocationTargetException) { 
     throw localInvocationTargetException.getCause(); 
    } 
    } 

이는 좀 봤와 others가보고 한 것을 발견 똑같은 일이지만 왜 이것이 사실인지에 대한 더 이상의 통찰력을 찾지 못했습니다.

+0

당신은 당신의 믿음에 대한 증거/증거를 제시 할 수 있습니까? 나는 내가 통제하지 못하는 유형에 추가 할 생각이었습니다. 그렇지 않으면 형질이 물론 더 나은 해결책입니다. 나는 그 점에 관해서 그 질문을 분명히한다. – Raphael

+0

@Raphael 여기에 scala가 설치되어 있지 않으며 증거를 줄 수도 없습니다. 몇 시간 후에 집에 갈 때 두 변종을 모두 컴파일하고 생성 된 .class를 확인한 다음 결과를 게시합니다. –

+1

@Raphael은 귀하가 테스트되지 않은 주장을 신뢰하지 않는 것으로 판명되었습니다. –

7

리플렉션을 사용하기 때문에 별도의 클래스를 사용하면 성능이 향상됩니다.

new { def foo(...) = { ... } } 

정말 이제

new AnyRef { def foo(...) = { ... } } 

것을 고려, AnyRef는 방법 foo이 없습니다. 스칼라에서이 유형은 실제로 AnyRef { def foo(...): ... }이며 AnyRef을 제거하면 구조 유형으로 인식해야합니다.

컴파일 타임에이 시간은 앞뒤로 전달 될 수 있으며 모든 곳에서 foo 메서드를 호출 할 수 있음을 알 수 있습니다. 그러나 JVM에는 구조적 유형이 없으므로 인터페이스를 추가하려면 프록시 객체가 필요하므로 참조 동등성을 깨는 것과 같은 문제가 발생합니다 (즉, 객체가 자체의 구조 유형 버전과 동일하지 않음).

주위에서 발견 된 방법은 구조적 유형에 대해 캐시 된 리플렉션 호출을 사용하는 것이 었습니다.

성능에 민감한 응용 프로그램에 Pimp My Library 패턴을 사용하려면 클래스를 선언하십시오.

+0

이러한 통찰력에 감사드립니다. 그러나이 설명이 paolo의 결과와 어떻게 일치하는지 궁금합니다. 이는 JVM에서 유형을 반영하지 않고 사용할 수 있음을 나타냅니다. 아니면'test $$ anon $ 1'은 클래스 이름 일뿐, 타입이 아닙니다. – Raphael

+1

@Raphael 인터페이스와 구현의 차이점과 같습니다. 'test $$ anon $ 1' 클래스는 인터페이스의 구현으로 생각할 수 있습니다. 반면 AnyRef {def foo (...) : ...}는 인터페이스 자체 일 것입니다. 제한된 시나리오에서 이러한 익명의 클래스로 리플렉션 호출을 대체 할 수는 있지만 더 일반적인 시나리오에는 해당되지 않습니다. 설명을 주신 덕분에 –

+0

. 나는'new {foo = "bar"}'가'new AnyRef {foo = "bar"}'가 아닌'new Compiler_Please_Create_This_Class_For_Me()'와 C#과 java의 익명 클래스와 동일하다고 가정하고 있었기 때문에 나는 그것을 발견했다. 익명 클래스를 구조 유형에 연결하기가 어렵습니다. –